From 512e5cb54dc0d562f3dae4e91ecfa96e37b69ef3 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 20 Mar 2020 12:13:58 +0100 Subject: [PATCH 001/146] cli: don't enforce prop-types for plugins --- packages/app/.eslintrc.js | 10 ++++++++++ packages/cli/config/eslint.js | 9 +++++++++ packages/core/.eslintrc.js | 10 +++++++++- 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 packages/app/.eslintrc.js diff --git a/packages/app/.eslintrc.js b/packages/app/.eslintrc.js new file mode 100644 index 0000000000..b321cd0fb8 --- /dev/null +++ b/packages/app/.eslintrc.js @@ -0,0 +1,10 @@ +module.exports = { + overrides: [ + { + files: ['**/*.ts?(x)'], + rules: { + 'react/prop-types': 1, + }, + }, + ], +}; diff --git a/packages/cli/config/eslint.js b/packages/cli/config/eslint.js index e6e81d4de5..32d1e220f7 100644 --- a/packages/cli/config/eslint.js +++ b/packages/cli/config/eslint.js @@ -39,4 +39,13 @@ module.exports = { }, }, ignorePatterns: ['**/dist/**', '**/build/**'], + overrides: [ + { + files: ['**/*.ts?(x)'], + rules: { + // Default to not enforcing prop-types in typescript + 'react/prop-types': 0, + }, + }, + ], }; diff --git a/packages/core/.eslintrc.js b/packages/core/.eslintrc.js index 11e2e9fc3c..e8b6139c06 100644 --- a/packages/core/.eslintrc.js +++ b/packages/core/.eslintrc.js @@ -1,6 +1,14 @@ module.exports = { + overrides: [ + { + files: ['**/*.ts?(x)'], + rules: { + // TODO: add prop types and set to 1 + 'react/prop-types': 0, + }, + }, + ], rules: { - 'react/prop-types': 0, 'jest/expect-expect': 0, }, }; From 4609c8f4e008bcf0d2e61512032fccf9a26c6847 Mon Sep 17 00:00:00 2001 From: Muhammad Rivki Date: Sat, 21 Mar 2020 21:55:31 +0700 Subject: [PATCH 002/146] feat: integration with storybook for @core package --- packages/core/.storybook/main.js | 19 + packages/core/package.json | 13 +- .../components/Progress/Progress.stories.tsx | 25 + yarn.lock | 2226 ++++++++++++++++- 4 files changed, 2194 insertions(+), 89 deletions(-) create mode 100644 packages/core/.storybook/main.js create mode 100644 packages/core/src/components/Progress/Progress.stories.tsx diff --git a/packages/core/.storybook/main.js b/packages/core/.storybook/main.js new file mode 100644 index 0000000000..f94725055c --- /dev/null +++ b/packages/core/.storybook/main.js @@ -0,0 +1,19 @@ +module.exports = { + stories: [ + '../src/layout/**/*.stories.tsx', + '../src/components/**/*.stories.tsx', + ], + addons: ['@storybook/addon-actions', '@storybook/addon-links'], + webpackFinal: async config => { + config.module.rules.push({ + test: /\.(ts|tsx)$/, + use: [ + { + loader: require.resolve('ts-loader'), + }, + ], + }); + config.resolve.extensions.push('.ts', '.tsx'); + return config; + }, +}; diff --git a/packages/core/package.json b/packages/core/package.json index b6ceeb42f2..05ee131bc9 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -8,7 +8,9 @@ "scripts": { "build": "tsc --outDir dist/cjs --noEmit false --module CommonJS", "lint": "backstage-cli lint", - "test": "backstage-cli test" + "test": "backstage-cli test", + "storybook": "start-storybook -p 6006", + "build-storybook": "build-storybook" }, "dependencies": { "@material-ui/core": "^4.9.1", @@ -26,10 +28,17 @@ "recompose": "0.30.0" }, "devDependencies": { + "@babel/core": "^7.9.0", "@spotify-backstage/cli": "^0.1.0", + "@storybook/addon-actions": "^5.3.17", + "@storybook/addon-links": "^5.3.17", + "@storybook/addons": "^5.3.17", + "@storybook/react": "^5.3.17", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.3.2", "@testing-library/user-event": "^7.1.2", - "prop-types": "^15.7.2" + "babel-loader": "^8.1.0", + "prop-types": "^15.7.2", + "ts-loader": "^6.2.1" } } diff --git a/packages/core/src/components/Progress/Progress.stories.tsx b/packages/core/src/components/Progress/Progress.stories.tsx new file mode 100644 index 0000000000..27fa7195f3 --- /dev/null +++ b/packages/core/src/components/Progress/Progress.stories.tsx @@ -0,0 +1,25 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import Progress from '.'; + +export default { + title: 'Progress', + component: Progress, +}; + +export const progress = () => ; diff --git a/yarn.lock b/yarn.lock index dda14de215..e5de24c4ec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,13 @@ # yarn lockfile v1 +"@babel/code-frame@7.5.5": + version "7.5.5" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" + integrity sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw== + dependencies: + "@babel/highlight" "^7.0.0" + "@babel/code-frame@7.8.3", "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3": version "7.8.3" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" @@ -60,6 +67,28 @@ semver "^5.4.1" source-map "^0.5.0" +"@babel/core@^7.9.0": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.9.0.tgz#ac977b538b77e132ff706f3b8a4dbad09c03c56e" + integrity sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/generator" "^7.9.0" + "@babel/helper-module-transforms" "^7.9.0" + "@babel/helpers" "^7.9.0" + "@babel/parser" "^7.9.0" + "@babel/template" "^7.8.6" + "@babel/traverse" "^7.9.0" + "@babel/types" "^7.9.0" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.13" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + "@babel/generator@^7.4.0", "@babel/generator@^7.8.4", "@babel/generator@^7.8.6": version "7.8.6" resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.8.6.tgz#57adf96d370c9a63c241cd719f9111468578537a" @@ -70,6 +99,16 @@ lodash "^4.17.13" source-map "^0.5.0" +"@babel/generator@^7.9.0": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.9.0.tgz#0f67adea4ec39dad6e63345f70eec33014d78c89" + integrity sha512-onl4Oy46oGCzymOXtKMQpI7VXtCbTSHK1kqBydZ6AmzuNcacEVqGk9tZtAS+48IA9IstZcDCgIg8hQKnb7suRw== + dependencies: + "@babel/types" "^7.9.0" + jsesc "^2.5.1" + lodash "^4.17.13" + source-map "^0.5.0" + "@babel/helper-annotate-as-pure@^7.8.3": version "7.8.3" resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz#60bc0bc657f63a0924ff9a4b4a0b24a13cf4deee" @@ -181,7 +220,7 @@ dependencies: "@babel/types" "^7.8.3" -"@babel/helper-module-imports@^7.8.3": +"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.8.3": version "7.8.3" resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz#7fe39589b39c016331b6b8c3f441e8f0b1419498" integrity sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg== @@ -201,6 +240,19 @@ "@babel/types" "^7.8.6" lodash "^4.17.13" +"@babel/helper-module-transforms@^7.9.0": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz#43b34dfe15961918707d247327431388e9fe96e5" + integrity sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA== + dependencies: + "@babel/helper-module-imports" "^7.8.3" + "@babel/helper-replace-supers" "^7.8.6" + "@babel/helper-simple-access" "^7.8.3" + "@babel/helper-split-export-declaration" "^7.8.3" + "@babel/template" "^7.8.6" + "@babel/types" "^7.9.0" + lodash "^4.17.13" + "@babel/helper-optimise-call-expression@^7.8.3": version "7.8.3" resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz#7ed071813d09c75298ef4f208956006b6111ecb9" @@ -256,6 +308,11 @@ dependencies: "@babel/types" "^7.8.3" +"@babel/helper-validator-identifier@^7.9.0": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz#ad53562a7fc29b3b9a91bbf7d10397fd146346ed" + integrity sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw== + "@babel/helper-wrap-function@^7.8.3": version "7.8.3" resolved "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz#9dbdb2bb55ef14aaa01fe8c99b629bd5352d8610" @@ -275,6 +332,24 @@ "@babel/traverse" "^7.8.4" "@babel/types" "^7.8.3" +"@babel/helpers@^7.9.0": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.0.tgz#ab2c1bc4821af766cab51d4868a5038874ea5a12" + integrity sha512-/9GvfYTCG1NWCNwDj9e+XlnSCmWW/r9T794Xi58vPF9WCcnZCAZ0kWLSn54oqP40SUvh1T2G6VwKmFO5AOlW3A== + dependencies: + "@babel/template" "^7.8.3" + "@babel/traverse" "^7.9.0" + "@babel/types" "^7.9.0" + +"@babel/highlight@^7.0.0": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz#4e9b45ccb82b79607271b2979ad82c7b68163079" + integrity sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ== + dependencies: + "@babel/helper-validator-identifier" "^7.9.0" + chalk "^2.0.0" + js-tokens "^4.0.0" + "@babel/highlight@^7.8.3": version "7.8.3" resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797" @@ -289,6 +364,11 @@ resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.8.6.tgz#ba5c9910cddb77685a008e3c587af8d27b67962c" integrity sha512-trGNYSfwq5s0SgM1BMEB8hX3NDmO7EP2wsDGDexiaKMB92BaRpS+qZfpkMqUBhcsOTBwNy9B/jieo4ad/t/z2g== +"@babel/parser@^7.9.0": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.9.0.tgz#f821b32313f07ee570976d3f6238e8d2d66e0a8e" + integrity sha512-Iwyp00CZsypoNJcpXCbq3G4tcDgphtlMwMVrMhhZ//XBkqjXF7LW6V511yk0+pBX3ZwwGnPea+pTKNJiqA7pUg== + "@babel/plugin-proposal-async-generator-functions@^7.8.3": version "7.8.3" resolved "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz#bad329c670b382589721b27540c7d288601c6e6f" @@ -298,7 +378,7 @@ "@babel/helper-remap-async-to-generator" "^7.8.3" "@babel/plugin-syntax-async-generators" "^7.8.0" -"@babel/plugin-proposal-class-properties@7.8.3": +"@babel/plugin-proposal-class-properties@7.8.3", "@babel/plugin-proposal-class-properties@^7.7.0": version "7.8.3" resolved "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.8.3.tgz#5e06654af5cd04b608915aada9b2a6788004464e" integrity sha512-EqFhbo7IosdgPgZggHaNObkmO1kNUe3slaKu54d5OWvy+p9QIKOzK1GAEpAIsZtWVtPXUHSMcT4smvDrCfY4AA== @@ -347,6 +427,14 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-numeric-separator" "^7.8.3" +"@babel/plugin-proposal-object-rest-spread@^7.6.2": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.0.tgz#a28993699fc13df165995362693962ba6b061d6f" + integrity sha512-UgqBv6bjq4fDb8uku9f+wcm1J7YxJ5nT7WO/jBr0cl0PLKb7t1O6RNR1kZbjgx2LQtsDI9hwoQVmn0yhXeQyow== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-proposal-object-rest-spread@^7.8.3": version "7.8.3" resolved "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.8.3.tgz#eb5ae366118ddca67bed583b53d7554cad9951bb" @@ -400,7 +488,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-syntax-dynamic-import@^7.8.0": +"@babel/plugin-syntax-dynamic-import@^7.2.0", "@babel/plugin-syntax-dynamic-import@^7.8.0": version "7.8.3" resolved "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== @@ -567,6 +655,14 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-flow" "^7.8.3" +"@babel/plugin-transform-flow-strip-types@^7.9.0": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.9.0.tgz#8a3538aa40434e000b8f44a3c5c9ac7229bd2392" + integrity sha512-7Qfg0lKQhEHs93FChxVLAvhBshOPQDtJUTVHr/ZwQNRccCm4O9D79r9tVSoV8iNwjP1YgfD+e/fgHcPkN1qEQg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-flow" "^7.8.3" + "@babel/plugin-transform-for-of@^7.8.4", "@babel/plugin-transform-for-of@^7.8.6": version "7.8.6" resolved "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.8.6.tgz#a051bd1b402c61af97a27ff51b468321c7c2a085" @@ -679,6 +775,13 @@ "@babel/helper-annotate-as-pure" "^7.8.3" "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-transform-react-constant-elements@^7.2.0", "@babel/plugin-transform-react-constant-elements@^7.6.3": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.9.0.tgz#a75abc936a3819edec42d3386d9f1c93f28d9d9e" + integrity sha512-wXMXsToAUOxJuBBEHajqKLFWcCkOSLshTI2ChCFFj1zDd7od4IOxiwLCOObNUvOpkxLpjIuaIdBMmNt6ocCPAw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-transform-react-display-name@7.8.3", "@babel/plugin-transform-react-display-name@^7.8.3": version "7.8.3" resolved "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.8.3.tgz#70ded987c91609f78353dd76d2fb2a0bb991e8e5" @@ -915,6 +1018,14 @@ levenary "^1.1.1" semver "^5.5.0" +"@babel/preset-flow@^7.0.0": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.9.0.tgz#fee847c3e090b0b2d9227c1949e4da1d1379280d" + integrity sha512-88uSmlshIrlmPkNkEcx3UpSZ6b8n0UGBq0/0ZMZCF/uxAW0XIAUuDHBhIOAh0pvweafH4RxOwi/H3rWhtqOYPA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-transform-flow-strip-types" "^7.9.0" + "@babel/preset-react@7.8.3", "@babel/preset-react@^7.0.0": version "7.8.3" resolved "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.8.3.tgz#23dc63f1b5b0751283e04252e78cf1d6589273d2" @@ -941,6 +1052,13 @@ dependencies: regenerator-runtime "^0.13.2" +"@babel/runtime@^7.5.0", "@babel/runtime@^7.7.4", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.7": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.0.tgz#337eda67401f5b066a6f205a3113d4ac18ba495b" + integrity sha512-cTIudHnzuWLS56ik4DnRnqqNf8MkdUzV4iFFI1h7Jo9xvrpQROYaAnaSd2mHLQAzzZAPfATynX5ord6YlNYNMA== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.4.0", "@babel/template@^7.7.4", "@babel/template@^7.8.3", "@babel/template@^7.8.6": version "7.8.6" resolved "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b" @@ -965,6 +1083,21 @@ globals "^11.1.0" lodash "^4.17.13" +"@babel/traverse@^7.9.0": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.0.tgz#d3882c2830e513f4fe4cec9fe76ea1cc78747892" + integrity sha512-jAZQj0+kn4WTHO5dUZkZKhbFrqZE7K5LAQ5JysMnmvGij+wOdr+8lWqPeW0BcF4wFwrEXXtdGO7wcV6YPJcf3w== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/generator" "^7.9.0" + "@babel/helper-function-name" "^7.8.3" + "@babel/helper-split-export-declaration" "^7.8.3" + "@babel/parser" "^7.9.0" + "@babel/types" "^7.9.0" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.13" + "@babel/types@^7.0.0", "@babel/types@^7.3.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.8.3", "@babel/types@^7.8.6": version "7.8.6" resolved "https://registry.npmjs.org/@babel/types/-/types-7.8.6.tgz#629ecc33c2557fcde7126e58053127afdb3e6d01" @@ -974,6 +1107,15 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" +"@babel/types@^7.9.0": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.9.0.tgz#00b064c3df83ad32b2dbf5ff07312b15c7f1efb5" + integrity sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng== + dependencies: + "@babel/helper-validator-identifier" "^7.9.0" + lodash "^4.17.13" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -1127,11 +1269,113 @@ resolved "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-10.1.0.tgz#f0950bba18819512d42f7197e56c518aa491cf18" integrity sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg== +"@emotion/cache@^10.0.27": + version "10.0.29" + resolved "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz#87e7e64f412c060102d589fe7c6dc042e6f9d1e0" + integrity sha512-fU2VtSVlHiF27empSbxi1O2JFdNWZO+2NFHfwO0pxgTep6Xa3uGb+3pVKfLww2l/IBGLNEZl5Xf/++A4wAYDYQ== + dependencies: + "@emotion/sheet" "0.9.4" + "@emotion/stylis" "0.8.5" + "@emotion/utils" "0.11.3" + "@emotion/weak-memoize" "0.2.5" + +"@emotion/core@^10.0.20": + version "10.0.28" + resolved "https://registry.npmjs.org/@emotion/core/-/core-10.0.28.tgz#bb65af7262a234593a9e952c041d0f1c9b9bef3d" + integrity sha512-pH8UueKYO5jgg0Iq+AmCLxBsvuGtvlmiDCOuv8fGNYn3cowFpLN98L8zO56U0H1PjDIyAlXymgL3Wu7u7v6hbA== + dependencies: + "@babel/runtime" "^7.5.5" + "@emotion/cache" "^10.0.27" + "@emotion/css" "^10.0.27" + "@emotion/serialize" "^0.11.15" + "@emotion/sheet" "0.9.4" + "@emotion/utils" "0.11.3" + +"@emotion/css@^10.0.27": + version "10.0.27" + resolved "https://registry.npmjs.org/@emotion/css/-/css-10.0.27.tgz#3a7458198fbbebb53b01b2b87f64e5e21241e14c" + integrity sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw== + dependencies: + "@emotion/serialize" "^0.11.15" + "@emotion/utils" "0.11.3" + babel-plugin-emotion "^10.0.27" + +"@emotion/hash@0.8.0": + version "0.8.0" + resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" + integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== + "@emotion/hash@^0.7.4": version "0.7.4" resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.7.4.tgz#f14932887422c9056b15a8d222a9074a7dfa2831" integrity sha512-fxfMSBMX3tlIbKUdtGKxqB1fyrH6gVrX39Gsv3y8lRYKUqlgDt3UMqQyGnR1bQMa2B8aGnhLZokZgg8vT0Le+A== +"@emotion/is-prop-valid@0.8.8": + version "0.8.8" + resolved "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a" + integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA== + dependencies: + "@emotion/memoize" "0.7.4" + +"@emotion/memoize@0.7.4": + version "0.7.4" + resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb" + integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw== + +"@emotion/serialize@^0.11.15", "@emotion/serialize@^0.11.16": + version "0.11.16" + resolved "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.16.tgz#dee05f9e96ad2fb25a5206b6d759b2d1ed3379ad" + integrity sha512-G3J4o8by0VRrO+PFeSc3js2myYNOXVJ3Ya+RGVxnshRYgsvErfAOglKAiy1Eo1vhzxqtUvjCyS5gtewzkmvSSg== + dependencies: + "@emotion/hash" "0.8.0" + "@emotion/memoize" "0.7.4" + "@emotion/unitless" "0.7.5" + "@emotion/utils" "0.11.3" + csstype "^2.5.7" + +"@emotion/sheet@0.9.4": + version "0.9.4" + resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.4.tgz#894374bea39ec30f489bbfc3438192b9774d32e5" + integrity sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA== + +"@emotion/styled-base@^10.0.27": + version "10.0.31" + resolved "https://registry.npmjs.org/@emotion/styled-base/-/styled-base-10.0.31.tgz#940957ee0aa15c6974adc7d494ff19765a2f742a" + integrity sha512-wTOE1NcXmqMWlyrtwdkqg87Mu6Rj1MaukEoEmEkHirO5IoHDJ8LgCQL4MjJODgxWxXibGR3opGp1p7YvkNEdXQ== + dependencies: + "@babel/runtime" "^7.5.5" + "@emotion/is-prop-valid" "0.8.8" + "@emotion/serialize" "^0.11.15" + "@emotion/utils" "0.11.3" + +"@emotion/styled@^10.0.17": + version "10.0.27" + resolved "https://registry.npmjs.org/@emotion/styled/-/styled-10.0.27.tgz#12cb67e91f7ad7431e1875b1d83a94b814133eaf" + integrity sha512-iK/8Sh7+NLJzyp9a5+vIQIXTYxfT4yB/OJbjzQanB2RZpvmzBQOHZWhpAMZWYEKRNNbsD6WfBw5sVWkb6WzS/Q== + dependencies: + "@emotion/styled-base" "^10.0.27" + babel-plugin-emotion "^10.0.27" + +"@emotion/stylis@0.8.5": + version "0.8.5" + resolved "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz#deacb389bd6ee77d1e7fcaccce9e16c5c7e78e04" + integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ== + +"@emotion/unitless@0.7.5": + version "0.7.5" + resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" + integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== + +"@emotion/utils@0.11.3": + version "0.11.3" + resolved "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz#a759863867befa7e583400d322652a3f44820924" + integrity sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw== + +"@emotion/weak-memoize@0.2.5": + version "0.2.5" + resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46" + integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA== + "@evocateur/libnpmaccess@^3.1.2": version "3.1.2" resolved "https://registry.npmjs.org/@evocateur/libnpmaccess/-/libnpmaccess-3.1.2.tgz#ecf7f6ce6b004e9f942b098d92200be4a4b1c845" @@ -2517,6 +2761,16 @@ dependencies: "@types/node" ">= 8" +"@reach/router@^1.2.1": + version "1.3.3" + resolved "https://registry.npmjs.org/@reach/router/-/router-1.3.3.tgz#58162860dce6c9449d49be86b0561b5ef46d80db" + integrity sha512-gOIAiFhWdiVGSVjukKeNKkCRBLmnORoTPyBihI/jLunICPgxdP30DroAvPQuf1eVfQbfGJQDJkwhJXsNPMnVWw== + dependencies: + create-react-context "0.3.0" + invariant "^2.2.3" + prop-types "^15.6.1" + react-lifecycles-compat "^3.0.4" + "@samverschueren/stream-to-observable@^0.3.0": version "0.3.0" resolved "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f" @@ -2690,6 +2944,361 @@ ts-jest "^25.0.0" typescript "^3.7.4" +"@storybook/addon-actions@^5.3.17": + version "5.3.17" + resolved "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-5.3.17.tgz#ec7ae8fa25ef211c2a3302b6ac1d271a6247f767" + integrity sha512-06HQSBqWFyXcqV418Uv3oMHomNy9g3uCt0FHrqY3BAc7PldY1X0tW65oy//uBueaRaYKdhtRrrjfXRaPQWmDbA== + dependencies: + "@storybook/addons" "5.3.17" + "@storybook/api" "5.3.17" + "@storybook/client-api" "5.3.17" + "@storybook/components" "5.3.17" + "@storybook/core-events" "5.3.17" + "@storybook/theming" "5.3.17" + core-js "^3.0.1" + fast-deep-equal "^2.0.1" + global "^4.3.2" + polished "^3.3.1" + prop-types "^15.7.2" + react "^16.8.3" + react-inspector "^4.0.0" + uuid "^3.3.2" + +"@storybook/addon-links@^5.3.17": + version "5.3.17" + resolved "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-5.3.17.tgz#bccbd49108f03eb8f7a72106fbbceed671e042a9" + integrity sha512-g8PsDoHYEmgK1q1h+eLqXsWLhCj5CFyiZLrtomjCp1R0XpZA7PS8LyO5yHbxzEzEv68DHlU5rwQnNnkbGnr7XA== + dependencies: + "@storybook/addons" "5.3.17" + "@storybook/client-logger" "5.3.17" + "@storybook/core-events" "5.3.17" + "@storybook/csf" "0.0.1" + "@storybook/router" "5.3.17" + core-js "^3.0.1" + global "^4.3.2" + prop-types "^15.7.2" + qs "^6.6.0" + ts-dedent "^1.1.0" + +"@storybook/addons@5.3.17", "@storybook/addons@^5.3.17": + version "5.3.17" + resolved "https://registry.npmjs.org/@storybook/addons/-/addons-5.3.17.tgz#8efab65904040b0b8578eedc9a5772dbcbf6fa83" + integrity sha512-zg6O1bmffRsHXJOWAnSD2O3tPnVMoD8Yfu+a5zBVXDiUP1E/TGzgjjjYBUUCU3yQg1Ted5rIn4o6ql/rZNNlgA== + dependencies: + "@storybook/api" "5.3.17" + "@storybook/channels" "5.3.17" + "@storybook/client-logger" "5.3.17" + "@storybook/core-events" "5.3.17" + core-js "^3.0.1" + global "^4.3.2" + util-deprecate "^1.0.2" + +"@storybook/api@5.3.17": + version "5.3.17" + resolved "https://registry.npmjs.org/@storybook/api/-/api-5.3.17.tgz#1c0dad3309afef6b0a5585cb59c65824fb4d2721" + integrity sha512-G40jtXFY10hQo6GSw5JeFYt41loD4+7s0uU18Rm6lfa/twOgp6vqqyDCWDvpRRxRBB5uDIKKHLt13X9gWe8tQQ== + dependencies: + "@reach/router" "^1.2.1" + "@storybook/channels" "5.3.17" + "@storybook/client-logger" "5.3.17" + "@storybook/core-events" "5.3.17" + "@storybook/csf" "0.0.1" + "@storybook/router" "5.3.17" + "@storybook/theming" "5.3.17" + "@types/reach__router" "^1.2.3" + core-js "^3.0.1" + fast-deep-equal "^2.0.1" + global "^4.3.2" + lodash "^4.17.15" + memoizerific "^1.11.3" + prop-types "^15.6.2" + react "^16.8.3" + semver "^6.0.0" + shallow-equal "^1.1.0" + store2 "^2.7.1" + telejson "^3.2.0" + util-deprecate "^1.0.2" + +"@storybook/channel-postmessage@5.3.17": + version "5.3.17" + resolved "https://registry.npmjs.org/@storybook/channel-postmessage/-/channel-postmessage-5.3.17.tgz#807b6316cd0e52d9f27363d5092ad1cd896b694c" + integrity sha512-1aSQNeO2+roPRgMFjW3AWTO3uS93lbCMUTYCBdi20md4bQ9SutJy33rynCQcWuMj1prCQ2Ekz4BGhdcIQVKlzg== + dependencies: + "@storybook/channels" "5.3.17" + "@storybook/client-logger" "5.3.17" + core-js "^3.0.1" + global "^4.3.2" + telejson "^3.2.0" + +"@storybook/channels@5.3.17": + version "5.3.17" + resolved "https://registry.npmjs.org/@storybook/channels/-/channels-5.3.17.tgz#74eccb10c2395499da6a290bcd0272d6d6c7c5b2" + integrity sha512-5hlBRbyk+YxC4KgecYG8wWwB2v1BzRJXhSlemFDOQk9wx37gVpne+rBydEtNFO4InmaZf6tKbBcpH0wBFLdWYA== + dependencies: + core-js "^3.0.1" + +"@storybook/client-api@5.3.17": + version "5.3.17" + resolved "https://registry.npmjs.org/@storybook/client-api/-/client-api-5.3.17.tgz#fc1d247caf267ebcc6ddf957fca7e02ae752d99e" + integrity sha512-oe55FPTGVL2k+j45eCN3oE7ePkE4VpgUQ/dhJbjU0R2L+HyRyBhd0wnMYj1f5E8uVNbtjFYAtbjjgcf1R1imeg== + dependencies: + "@storybook/addons" "5.3.17" + "@storybook/channel-postmessage" "5.3.17" + "@storybook/channels" "5.3.17" + "@storybook/client-logger" "5.3.17" + "@storybook/core-events" "5.3.17" + "@storybook/csf" "0.0.1" + "@types/webpack-env" "^1.15.0" + core-js "^3.0.1" + eventemitter3 "^4.0.0" + global "^4.3.2" + is-plain-object "^3.0.0" + lodash "^4.17.15" + memoizerific "^1.11.3" + qs "^6.6.0" + stable "^0.1.8" + ts-dedent "^1.1.0" + util-deprecate "^1.0.2" + +"@storybook/client-logger@5.3.17": + version "5.3.17" + resolved "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-5.3.17.tgz#bf9c7ef52da75a5c1f2c5d74724442224deea6e4" + integrity sha512-GYYvVGIOs+fq11LXXy7x2sr3hhC9LMI1jtIckjKV1dsY9MJ5g22M+Wl5Iw4nf6VMWsqcN9LSlYE+u/H+Q2uCHw== + dependencies: + core-js "^3.0.1" + +"@storybook/components@5.3.17": + version "5.3.17" + resolved "https://registry.npmjs.org/@storybook/components/-/components-5.3.17.tgz#287430fc9c5f59b1d3590b50b3c7688355b22639" + integrity sha512-M5oqbzcqFX4VDNI8siT3phT7rmFwChQ/xPwX9ygByBsZCoNuLMzafavfTOhZvxCPiliFbBxmxtK/ibCsSzuKZg== + dependencies: + "@storybook/client-logger" "5.3.17" + "@storybook/theming" "5.3.17" + "@types/react-syntax-highlighter" "11.0.4" + "@types/react-textarea-autosize" "^4.3.3" + core-js "^3.0.1" + global "^4.3.2" + lodash "^4.17.15" + markdown-to-jsx "^6.9.1" + memoizerific "^1.11.3" + polished "^3.3.1" + popper.js "^1.14.7" + prop-types "^15.7.2" + react "^16.8.3" + react-dom "^16.8.3" + react-focus-lock "^2.1.0" + react-helmet-async "^1.0.2" + react-popper-tooltip "^2.8.3" + react-syntax-highlighter "^11.0.2" + react-textarea-autosize "^7.1.0" + simplebar-react "^1.0.0-alpha.6" + ts-dedent "^1.1.0" + +"@storybook/core-events@5.3.17": + version "5.3.17" + resolved "https://registry.npmjs.org/@storybook/core-events/-/core-events-5.3.17.tgz#698ce0a36c29fe8fa04608f56ccca53aa1d31638" + integrity sha512-DOeX9fpeGW4o9Gocxa4VW9wAlAyfIVNDTzq0wVvvMBthTTo9u58NmndglEMDgDa2Cq6iAIPh7vz2bRJCNexzLw== + dependencies: + core-js "^3.0.1" + +"@storybook/core@5.3.17": + version "5.3.17" + resolved "https://registry.npmjs.org/@storybook/core/-/core-5.3.17.tgz#abd09dc416f87c7954ef3615bc3f4898c93e2b45" + integrity sha512-H6G8ygjb4RSVSKPdWz6su3Nvzxm8CfrHuCyUo4DLC46mirXfYRrJV1HiwXriViqoZV4gFbpaNKTDzTl/QKFDAg== + dependencies: + "@babel/plugin-proposal-class-properties" "^7.7.0" + "@babel/plugin-proposal-object-rest-spread" "^7.6.2" + "@babel/plugin-syntax-dynamic-import" "^7.2.0" + "@babel/plugin-transform-react-constant-elements" "^7.2.0" + "@babel/preset-env" "^7.4.5" + "@storybook/addons" "5.3.17" + "@storybook/channel-postmessage" "5.3.17" + "@storybook/client-api" "5.3.17" + "@storybook/client-logger" "5.3.17" + "@storybook/core-events" "5.3.17" + "@storybook/csf" "0.0.1" + "@storybook/node-logger" "5.3.17" + "@storybook/router" "5.3.17" + "@storybook/theming" "5.3.17" + "@storybook/ui" "5.3.17" + airbnb-js-shims "^2.2.1" + ansi-to-html "^0.6.11" + autoprefixer "^9.7.2" + babel-plugin-add-react-displayname "^0.0.5" + babel-plugin-emotion "^10.0.20" + babel-plugin-macros "^2.7.0" + babel-preset-minify "^0.5.0 || 0.6.0-alpha.5" + boxen "^4.1.0" + case-sensitive-paths-webpack-plugin "^2.2.0" + chalk "^3.0.0" + cli-table3 "0.5.1" + commander "^4.0.1" + core-js "^3.0.1" + corejs-upgrade-webpack-plugin "^2.2.0" + css-loader "^3.0.0" + detect-port "^1.3.0" + dotenv-webpack "^1.7.0" + ejs "^2.7.4" + express "^4.17.0" + file-loader "^4.2.0" + file-system-cache "^1.0.5" + find-cache-dir "^3.0.0" + find-up "^4.1.0" + fs-extra "^8.0.1" + glob-base "^0.3.0" + global "^4.3.2" + html-webpack-plugin "^4.0.0-beta.2" + inquirer "^7.0.0" + interpret "^2.0.0" + ip "^1.1.5" + json5 "^2.1.1" + lazy-universal-dotenv "^3.0.1" + micromatch "^4.0.2" + node-fetch "^2.6.0" + open "^7.0.0" + pnp-webpack-plugin "1.5.0" + postcss-flexbugs-fixes "^4.1.0" + postcss-loader "^3.0.0" + pretty-hrtime "^1.0.3" + qs "^6.6.0" + raw-loader "^3.1.0" + react-dev-utils "^9.0.0" + regenerator-runtime "^0.13.3" + resolve "^1.11.0" + resolve-from "^5.0.0" + semver "^6.0.0" + serve-favicon "^2.5.0" + shelljs "^0.8.3" + style-loader "^1.0.0" + terser-webpack-plugin "^2.1.2" + ts-dedent "^1.1.0" + unfetch "^4.1.0" + url-loader "^2.0.1" + util-deprecate "^1.0.2" + webpack "^4.33.0" + webpack-dev-middleware "^3.7.0" + webpack-hot-middleware "^2.25.0" + webpack-virtual-modules "^0.2.0" + +"@storybook/csf@0.0.1": + version "0.0.1" + resolved "https://registry.npmjs.org/@storybook/csf/-/csf-0.0.1.tgz#95901507dc02f0bc6f9ac8ee1983e2fc5bb98ce6" + integrity sha512-USTLkZze5gkel8MYCujSRBVIrUQ3YPBrLOx7GNk/0wttvVtlzWXAq9eLbQ4p/NicGxP+3T7KPEMVV//g+yubpw== + dependencies: + lodash "^4.17.15" + +"@storybook/node-logger@5.3.17": + version "5.3.17" + resolved "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-5.3.17.tgz#f3ad5bf9dd74d8e1cdfb8d831d66a80c5039cf4c" + integrity sha512-onfcxl37BYZI1HGuPI9MelkyUWjn7NpfN8RUYdqG9P6WKiIY5xbpG0V6qod5jvIKIypK0NmfJTtneOu46L/oDg== + dependencies: + "@types/npmlog" "^4.1.2" + chalk "^3.0.0" + core-js "^3.0.1" + npmlog "^4.1.2" + pretty-hrtime "^1.0.3" + regenerator-runtime "^0.13.3" + +"@storybook/react@^5.3.17": + version "5.3.17" + resolved "https://registry.npmjs.org/@storybook/react/-/react-5.3.17.tgz#403b58606211a9ca87d40e38d55186b3e088640d" + integrity sha512-FQLH3q2Ge68oLBaTge7wl5Y1KkB+pqL36llor7TOO9IxGLF6o2t2qillWnrgX6yZUpkvJK8MgkZW1/N3tslw4Q== + dependencies: + "@babel/plugin-transform-react-constant-elements" "^7.6.3" + "@babel/preset-flow" "^7.0.0" + "@babel/preset-react" "^7.0.0" + "@storybook/addons" "5.3.17" + "@storybook/core" "5.3.17" + "@storybook/node-logger" "5.3.17" + "@svgr/webpack" "^4.0.3" + "@types/webpack-env" "^1.15.0" + babel-plugin-add-react-displayname "^0.0.5" + babel-plugin-named-asset-import "^0.3.1" + babel-plugin-react-docgen "^4.0.0" + core-js "^3.0.1" + global "^4.3.2" + lodash "^4.17.15" + mini-css-extract-plugin "^0.7.0" + prop-types "^15.7.2" + react-dev-utils "^9.0.0" + regenerator-runtime "^0.13.3" + semver "^6.0.0" + ts-dedent "^1.1.0" + webpack "^4.33.0" + +"@storybook/router@5.3.17": + version "5.3.17" + resolved "https://registry.npmjs.org/@storybook/router/-/router-5.3.17.tgz#4db96b45f39b25a3f7a4e2899c36e7e9e4ba6108" + integrity sha512-ANsiehGRTVSremgTW0Vt47dQ4JA86a4/w/4G6QqHU8Cm4jO3cw/wAcCxlzfcgCXOUiq+SuyPTU43+0O5uBx33g== + dependencies: + "@reach/router" "^1.2.1" + "@storybook/csf" "0.0.1" + "@types/reach__router" "^1.2.3" + core-js "^3.0.1" + global "^4.3.2" + lodash "^4.17.15" + memoizerific "^1.11.3" + qs "^6.6.0" + util-deprecate "^1.0.2" + +"@storybook/theming@5.3.17": + version "5.3.17" + resolved "https://registry.npmjs.org/@storybook/theming/-/theming-5.3.17.tgz#cf6278c4857229c7167faf04d5b2206bc5ee04e1" + integrity sha512-4JeOZnDDHtb4LOt5sXe/s1Jhbb2UPsr8zL9NWmKJmTsgnyTvBipNHOmFYDUsIacB5K4GXSqm+cZ7Z4AkUgWCDw== + dependencies: + "@emotion/core" "^10.0.20" + "@emotion/styled" "^10.0.17" + "@storybook/client-logger" "5.3.17" + core-js "^3.0.1" + deep-object-diff "^1.1.0" + emotion-theming "^10.0.19" + global "^4.3.2" + memoizerific "^1.11.3" + polished "^3.3.1" + prop-types "^15.7.2" + resolve-from "^5.0.0" + ts-dedent "^1.1.0" + +"@storybook/ui@5.3.17": + version "5.3.17" + resolved "https://registry.npmjs.org/@storybook/ui/-/ui-5.3.17.tgz#2d47617896a2d928fb79dc8a0e709cee9b57cc50" + integrity sha512-5S9r70QbtNKu8loa5pfO5lLX9coF/ZqesEKcanfvuSwqCSg/Z51UwFCuO6eNhVlpXzyZXi5d8qKbZlbf+uvDAA== + dependencies: + "@emotion/core" "^10.0.20" + "@storybook/addons" "5.3.17" + "@storybook/api" "5.3.17" + "@storybook/channels" "5.3.17" + "@storybook/client-logger" "5.3.17" + "@storybook/components" "5.3.17" + "@storybook/core-events" "5.3.17" + "@storybook/router" "5.3.17" + "@storybook/theming" "5.3.17" + copy-to-clipboard "^3.0.8" + core-js "^3.0.1" + core-js-pure "^3.0.1" + emotion-theming "^10.0.19" + fast-deep-equal "^2.0.1" + fuse.js "^3.4.6" + global "^4.3.2" + lodash "^4.17.15" + markdown-to-jsx "^6.9.3" + memoizerific "^1.11.3" + polished "^3.3.1" + prop-types "^15.7.2" + qs "^6.6.0" + react "^16.8.3" + react-dom "^16.8.3" + react-draggable "^4.0.3" + react-helmet-async "^1.0.2" + react-hotkeys "2.0.0" + react-sizeme "^2.6.7" + regenerator-runtime "^0.13.2" + resolve-from "^5.0.0" + semver "^6.0.0" + store2 "^2.7.1" + telejson "^3.2.0" + util-deprecate "^1.0.2" + "@svgr/babel-plugin-add-jsx-attribute@^4.2.0": version "4.2.0" resolved "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz#dadcb6218503532d6884b210e7f3c502caaa44b1" @@ -2779,7 +3388,7 @@ merge-deep "^3.0.2" svgo "^1.2.2" -"@svgr/webpack@4.3.3": +"@svgr/webpack@4.3.3", "@svgr/webpack@^4.0.3": version "4.3.3" resolved "https://registry.npmjs.org/@svgr/webpack/-/webpack-4.3.3.tgz#13cc2423bf3dff2d494f16b17eb7eacb86895017" integrity sha512-bjnWolZ6KVsHhgyCoYRFmbd26p8XVbulCzSG53BDQqAr+JOAderYK7CuYrB3bDjHJuF6LJ7Wrr42+goLRV9qIg== @@ -3037,6 +3646,11 @@ "@types/through" "*" rxjs "^6.4.0" +"@types/is-function@^1.0.0": + version "1.0.0" + resolved "https://registry.npmjs.org/@types/is-function/-/is-function-1.0.0.tgz#1b0b819b1636c7baf0d6785d030d12edf70c3e83" + integrity sha512-iTs9HReBu7evG77Q4EC8hZnqRt57irBDkK9nvmHroiOIVwYMQc4IvYvdRgwKfYepunIY7Oh/dBuuld+Gj9uo6w== + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.1" resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff" @@ -3115,6 +3729,11 @@ resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== +"@types/npmlog@^4.1.2": + version "4.1.2" + resolved "https://registry.npmjs.org/@types/npmlog/-/npmlog-4.1.2.tgz#d070fe6a6b78755d1092a3dc492d34c3d8f871c4" + integrity sha512-4QQmOF5KlwfxJ5IGXFIudkeLCdMABz03RcUXu+LCb24zmln8QW6aDjuGl4d4XPVLf2j+FnjelHTP7dvceAFbhA== + "@types/ora@^3.2.0": version "3.2.0" resolved "https://registry.npmjs.org/@types/ora/-/ora-3.2.0.tgz#b2f65d1283a8f36d8b0f9ee767e0732a2f429362" @@ -3142,6 +3761,14 @@ resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== +"@types/reach__router@^1.2.3": + version "1.3.1" + resolved "https://registry.npmjs.org/@types/reach__router/-/reach__router-1.3.1.tgz#ca8b431acb12bb897d2b806f6fdd815f056d6d02" + integrity sha512-E51ntVeunnxofXmOoPFiOvElHWf+jBEs3B56gGx7XhPHOkJdjWxWDY4V1AsUiwhtOCXPM7atFy30wj7glyv2Fg== + dependencies: + "@types/history" "*" + "@types/react" "*" + "@types/react-dev-utils@^9.0.4": version "9.0.4" resolved "https://registry.npmjs.org/@types/react-dev-utils/-/react-dev-utils-9.0.4.tgz#3e4bee79b7536777cef219427ab1d38adc24f3f2" @@ -3177,6 +3804,20 @@ "@types/history" "*" "@types/react" "*" +"@types/react-syntax-highlighter@11.0.4": + version "11.0.4" + resolved "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.4.tgz#d86d17697db62f98046874f62fdb3e53a0bbc4cd" + integrity sha512-9GfTo3a0PHwQeTVoqs0g5bS28KkSY48pp5659wA+Dp4MqceDEa8EHBqrllJvvtyusszyJhViUEap0FDvlk/9Zg== + dependencies: + "@types/react" "*" + +"@types/react-textarea-autosize@^4.3.3": + version "4.3.5" + resolved "https://registry.npmjs.org/@types/react-textarea-autosize/-/react-textarea-autosize-4.3.5.tgz#6c4d2753fa1864c98c0b2b517f67bb1f6e4c46de" + integrity sha512-PiDL83kPMTolyZAWW3lyzO6ktooTb9tFTntVy7CA83/qFLWKLJ5bLeRboy6J6j3b1e8h2Eec6gBTEOOJRjV14A== + dependencies: + "@types/react" "*" + "@types/react-transition-group@^4.2.0": version "4.2.4" resolved "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.2.4.tgz#c7416225987ccdb719262766c1483da8f826838d" @@ -3279,6 +3920,11 @@ "@types/serve-static" "*" "@types/webpack" "*" +"@types/webpack-env@^1.15.0": + version "1.15.1" + resolved "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.15.1.tgz#c8e84705e08eed430b5e15b39c65b0944e4d1422" + integrity sha512-eWN5ElDTeBc5lRDh95SqA8x18D0ll2pWudU3uWiyfsRmIZcmUXpEsxPU+7+BsdCrO2vfLRC629u/MmjbmF+2tA== + "@types/webpack-sources@*": version "0.1.6" resolved "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.6.tgz#3d21dfc2ec0ad0c77758e79362426a9ba7d7cbcb" @@ -3691,6 +4337,29 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" +airbnb-js-shims@^2.2.1: + version "2.2.1" + resolved "https://registry.npmjs.org/airbnb-js-shims/-/airbnb-js-shims-2.2.1.tgz#db481102d682b98ed1daa4c5baa697a05ce5c040" + integrity sha512-wJNXPH66U2xjgo1Zwyjf9EydvJ2Si94+vSdk6EERcBfB2VZkeltpqIats0cqIZMLCXP3zcyaUKGYQeIBT6XjsQ== + dependencies: + array-includes "^3.0.3" + array.prototype.flat "^1.2.1" + array.prototype.flatmap "^1.2.1" + es5-shim "^4.5.13" + es6-shim "^0.35.5" + function.prototype.name "^1.1.0" + globalthis "^1.0.0" + object.entries "^1.1.0" + object.fromentries "^2.0.0 || ^1.0.0" + object.getownpropertydescriptors "^2.0.3" + object.values "^1.1.0" + promise.allsettled "^1.0.0" + promise.prototype.finally "^3.1.0" + string.prototype.matchall "^4.0.0 || ^3.0.1" + string.prototype.padend "^3.0.0" + string.prototype.padstart "^3.0.0" + symbol.prototype.description "^1.0.0" + ajv-errors@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" @@ -3701,7 +4370,7 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1: resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da" integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ== -ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.5.5: +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.0, ajv@^6.5.5: version "6.12.0" resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7" integrity sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw== @@ -3723,6 +4392,13 @@ ansi-align@^2.0.0: dependencies: string-width "^2.0.0" +ansi-align@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb" + integrity sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw== + dependencies: + string-width "^3.0.0" + ansi-colors@^3.0.0: version "3.2.4" resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" @@ -3785,6 +4461,13 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: "@types/color-name" "^1.1.1" color-convert "^2.0.1" +ansi-to-html@^0.6.11: + version "0.6.14" + resolved "https://registry.npmjs.org/ansi-to-html/-/ansi-to-html-0.6.14.tgz#65fe6d08bba5dd9db33f44a20aec331e0010dad8" + integrity sha512-7ZslfB1+EnFSDO5Ju+ue5Y6It19DRnZXWv8jrGHgIlPna5Mh4jz7BV5jCbQneXNFurQcKoolaaAjHtgSBfOIuA== + dependencies: + entities "^1.1.2" + ansicolors@~0.3.2: version "0.3.2" resolved "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979" @@ -3821,6 +4504,11 @@ anymatch@^3.0.3, anymatch@~3.1.1: normalize-path "^3.0.0" picomatch "^2.0.4" +app-root-dir@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/app-root-dir/-/app-root-dir-1.0.2.tgz#38187ec2dea7577fff033ffcb12172692ff6e118" + integrity sha1-OBh+wt6nV3//Az/8sSFyaS/24Rg= + aproba@^1.0.3, aproba@^1.1.1, aproba@^1.1.2: version "1.2.0" resolved "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -3958,6 +4646,25 @@ array.prototype.flat@^1.2.1: define-properties "^1.1.3" es-abstract "^1.17.0-next.1" +array.prototype.flatmap@^1.2.1: + version "1.2.3" + resolved "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.3.tgz#1c13f84a178566042dd63de4414440db9222e443" + integrity sha512-OOEk+lkePcg+ODXIpvuU9PAryCikCJyo7GlDG1upleEpQRx6mzL9puEBkozQ5iAx20KV0l3DbyQwqciJtqe5Pg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + +array.prototype.map@^1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.2.tgz#9a4159f416458a23e9483078de1106b2ef68f8ec" + integrity sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + es-array-method-boxes-properly "^1.0.0" + is-string "^1.0.4" + arrify@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -4014,6 +4721,16 @@ ast-types-flow@0.0.7, ast-types-flow@^0.0.7: resolved "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0= +ast-types@0.11.3: + version "0.11.3" + resolved "https://registry.npmjs.org/ast-types/-/ast-types-0.11.3.tgz#c20757fe72ee71278ea0ff3d87e5c2ca30d9edf8" + integrity sha512-XA5o5dsNw8MhyW0Q7MWXJWc4oOzZKbdsEJq45h7c8q/d9DwWZ5F2ugUc1PuMLPGsUnphCt/cNDHu8JeBbxf1qA== + +ast-types@^0.13.2: + version "0.13.2" + resolved "https://registry.npmjs.org/ast-types/-/ast-types-0.13.2.tgz#df39b677a911a83f3a049644fb74fdded23cea48" + integrity sha512-uWMHxJxtfj/1oZClOxDEV1sQ1HCDkA4MG8Gr69KKeBjEVH0R84WlejZ0y2DcwyBlpAEMltmVYkVgqfLFb2oyiA== + astral-regex@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" @@ -4051,7 +4768,7 @@ atob@^2.1.2: resolved "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -autoprefixer@^9.6.1: +autoprefixer@^9.6.1, autoprefixer@^9.7.2: version "9.7.4" resolved "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.7.4.tgz#f8bf3e06707d047f0641d87aee8cfb174b2a5378" integrity sha512-g0Ya30YrMBAEZk60lp+qfX5YQllG+S5W3GYCFvyHTvhOki0AEQJLPEcIuGRsqVwLi8FvXPVtwTGhfr38hVpm0g== @@ -4107,6 +4824,41 @@ babel-extract-comments@^1.0.0: dependencies: babylon "^6.18.0" +babel-helper-evaluate-path@^0.5.0: + version "0.5.0" + resolved "https://registry.npmjs.org/babel-helper-evaluate-path/-/babel-helper-evaluate-path-0.5.0.tgz#a62fa9c4e64ff7ea5cea9353174ef023a900a67c" + integrity sha512-mUh0UhS607bGh5wUMAQfOpt2JX2ThXMtppHRdRU1kL7ZLRWIXxoV2UIV1r2cAeeNeU1M5SB5/RSUgUxrK8yOkA== + +babel-helper-flip-expressions@^0.4.3: + version "0.4.3" + resolved "https://registry.npmjs.org/babel-helper-flip-expressions/-/babel-helper-flip-expressions-0.4.3.tgz#3696736a128ac18bc25254b5f40a22ceb3c1d3fd" + integrity sha1-NpZzahKKwYvCUlS19AoizrPB0/0= + +babel-helper-is-nodes-equiv@^0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/babel-helper-is-nodes-equiv/-/babel-helper-is-nodes-equiv-0.0.1.tgz#34e9b300b1479ddd98ec77ea0bbe9342dfe39684" + integrity sha1-NOmzALFHnd2Y7HfqC76TQt/jloQ= + +babel-helper-is-void-0@^0.4.3: + version "0.4.3" + resolved "https://registry.npmjs.org/babel-helper-is-void-0/-/babel-helper-is-void-0-0.4.3.tgz#7d9c01b4561e7b95dbda0f6eee48f5b60e67313e" + integrity sha1-fZwBtFYee5Xb2g9u7kj1tg5nMT4= + +babel-helper-mark-eval-scopes@^0.4.3: + version "0.4.3" + resolved "https://registry.npmjs.org/babel-helper-mark-eval-scopes/-/babel-helper-mark-eval-scopes-0.4.3.tgz#d244a3bef9844872603ffb46e22ce8acdf551562" + integrity sha1-0kSjvvmESHJgP/tG4izorN9VFWI= + +babel-helper-remove-or-void@^0.4.3: + version "0.4.3" + resolved "https://registry.npmjs.org/babel-helper-remove-or-void/-/babel-helper-remove-or-void-0.4.3.tgz#a4f03b40077a0ffe88e45d07010dee241ff5ae60" + integrity sha1-pPA7QAd6D/6I5F0HAQ3uJB/1rmA= + +babel-helper-to-multiple-sequence-expressions@^0.5.0: + version "0.5.0" + resolved "https://registry.npmjs.org/babel-helper-to-multiple-sequence-expressions/-/babel-helper-to-multiple-sequence-expressions-0.5.0.tgz#a3f924e3561882d42fcf48907aa98f7979a4588d" + integrity sha512-m2CvfDW4+1qfDdsrtf4dwOslQC3yhbgyBFptncp4wvtdrDHqueW7slsYv4gArie056phvQFhT2nRcGS4bnm6mA== + babel-jest@^24.9.0: version "24.9.0" resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-24.9.0.tgz#3fc327cb8467b89d14d7bc70e315104a783ccd54" @@ -4143,6 +4895,22 @@ babel-loader@8.0.6: mkdirp "^0.5.1" pify "^4.0.1" +babel-loader@^8.1.0: + version "8.1.0" + resolved "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz#c611d5112bd5209abe8b9fa84c3e4da25275f1c3" + integrity sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw== + dependencies: + find-cache-dir "^2.1.0" + loader-utils "^1.4.0" + mkdirp "^0.5.3" + pify "^4.0.1" + schema-utils "^2.6.5" + +babel-plugin-add-react-displayname@^0.0.5: + version "0.0.5" + resolved "https://registry.npmjs.org/babel-plugin-add-react-displayname/-/babel-plugin-add-react-displayname-0.0.5.tgz#339d4cddb7b65fd62d1df9db9fe04de134122bd5" + integrity sha1-M51M3be2X9YtHfnbn+BN4TQSK9U= + babel-plugin-dynamic-import-node@^2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f" @@ -4150,6 +4918,22 @@ babel-plugin-dynamic-import-node@^2.3.0: dependencies: object.assign "^4.1.0" +babel-plugin-emotion@^10.0.20, babel-plugin-emotion@^10.0.27: + version "10.0.29" + resolved "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-10.0.29.tgz#89d8e497091fcd3d10331f097f1471e4cc3f35b4" + integrity sha512-7Jpi1OCxjyz0k163lKtqP+LHMg5z3S6A7vMBfHnF06l2unmtsOmFDzZBpGf0CWo1G4m8UACfVcDJiSiRuu/cSw== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@emotion/hash" "0.8.0" + "@emotion/memoize" "0.7.4" + "@emotion/serialize" "^0.11.16" + babel-plugin-macros "^2.0.0" + babel-plugin-syntax-jsx "^6.18.0" + convert-source-map "^1.5.0" + escape-string-regexp "^1.0.5" + find-root "^1.1.0" + source-map "^0.5.7" + babel-plugin-istanbul@^5.1.0: version "5.2.0" resolved "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz#df4ade83d897a92df069c4d9a25cf2671293c854" @@ -4185,7 +4969,7 @@ babel-plugin-jest-hoist@^25.1.0: dependencies: "@types/babel__traverse" "^7.0.6" -babel-plugin-macros@2.8.0: +babel-plugin-macros@2.8.0, babel-plugin-macros@^2.0.0, babel-plugin-macros@^2.7.0: version "2.8.0" resolved "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz#0f958a7cc6556b1e65344465d99111a1e5e10138" integrity sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg== @@ -4194,16 +4978,126 @@ babel-plugin-macros@2.8.0: cosmiconfig "^6.0.0" resolve "^1.12.0" -babel-plugin-named-asset-import@^0.3.6: +babel-plugin-minify-builtins@^0.5.0: + version "0.5.0" + resolved "https://registry.npmjs.org/babel-plugin-minify-builtins/-/babel-plugin-minify-builtins-0.5.0.tgz#31eb82ed1a0d0efdc31312f93b6e4741ce82c36b" + integrity sha512-wpqbN7Ov5hsNwGdzuzvFcjgRlzbIeVv1gMIlICbPj0xkexnfoIDe7q+AZHMkQmAE/F9R5jkrB6TLfTegImlXag== + +babel-plugin-minify-constant-folding@^0.5.0: + version "0.5.0" + resolved "https://registry.npmjs.org/babel-plugin-minify-constant-folding/-/babel-plugin-minify-constant-folding-0.5.0.tgz#f84bc8dbf6a561e5e350ff95ae216b0ad5515b6e" + integrity sha512-Vj97CTn/lE9hR1D+jKUeHfNy+m1baNiJ1wJvoGyOBUx7F7kJqDZxr9nCHjO/Ad+irbR3HzR6jABpSSA29QsrXQ== + dependencies: + babel-helper-evaluate-path "^0.5.0" + +babel-plugin-minify-dead-code-elimination@^0.5.1: + version "0.5.1" + resolved "https://registry.npmjs.org/babel-plugin-minify-dead-code-elimination/-/babel-plugin-minify-dead-code-elimination-0.5.1.tgz#1a0c68e44be30de4976ca69ffc535e08be13683f" + integrity sha512-x8OJOZIrRmQBcSqxBcLbMIK8uPmTvNWPXH2bh5MDCW1latEqYiRMuUkPImKcfpo59pTUB2FT7HfcgtG8ZlR5Qg== + dependencies: + babel-helper-evaluate-path "^0.5.0" + babel-helper-mark-eval-scopes "^0.4.3" + babel-helper-remove-or-void "^0.4.3" + lodash "^4.17.11" + +babel-plugin-minify-flip-comparisons@^0.4.3: + version "0.4.3" + resolved "https://registry.npmjs.org/babel-plugin-minify-flip-comparisons/-/babel-plugin-minify-flip-comparisons-0.4.3.tgz#00ca870cb8f13b45c038b3c1ebc0f227293c965a" + integrity sha1-AMqHDLjxO0XAOLPB68DyJyk8llo= + dependencies: + babel-helper-is-void-0 "^0.4.3" + +babel-plugin-minify-guarded-expressions@^0.4.4: + version "0.4.4" + resolved "https://registry.npmjs.org/babel-plugin-minify-guarded-expressions/-/babel-plugin-minify-guarded-expressions-0.4.4.tgz#818960f64cc08aee9d6c75bec6da974c4d621135" + integrity sha512-RMv0tM72YuPPfLT9QLr3ix9nwUIq+sHT6z8Iu3sLbqldzC1Dls8DPCywzUIzkTx9Zh1hWX4q/m9BPoPed9GOfA== + dependencies: + babel-helper-evaluate-path "^0.5.0" + babel-helper-flip-expressions "^0.4.3" + +babel-plugin-minify-infinity@^0.4.3: + version "0.4.3" + resolved "https://registry.npmjs.org/babel-plugin-minify-infinity/-/babel-plugin-minify-infinity-0.4.3.tgz#dfb876a1b08a06576384ef3f92e653ba607b39ca" + integrity sha1-37h2obCKBldjhO8/kuZTumB7Oco= + +babel-plugin-minify-mangle-names@^0.5.0: + version "0.5.0" + resolved "https://registry.npmjs.org/babel-plugin-minify-mangle-names/-/babel-plugin-minify-mangle-names-0.5.0.tgz#bcddb507c91d2c99e138bd6b17a19c3c271e3fd3" + integrity sha512-3jdNv6hCAw6fsX1p2wBGPfWuK69sfOjfd3zjUXkbq8McbohWy23tpXfy5RnToYWggvqzuMOwlId1PhyHOfgnGw== + dependencies: + babel-helper-mark-eval-scopes "^0.4.3" + +babel-plugin-minify-numeric-literals@^0.4.3: + version "0.4.3" + resolved "https://registry.npmjs.org/babel-plugin-minify-numeric-literals/-/babel-plugin-minify-numeric-literals-0.4.3.tgz#8e4fd561c79f7801286ff60e8c5fd9deee93c0bc" + integrity sha1-jk/VYcefeAEob/YOjF/Z3u6TwLw= + +babel-plugin-minify-replace@^0.5.0: + version "0.5.0" + resolved "https://registry.npmjs.org/babel-plugin-minify-replace/-/babel-plugin-minify-replace-0.5.0.tgz#d3e2c9946c9096c070efc96761ce288ec5c3f71c" + integrity sha512-aXZiaqWDNUbyNNNpWs/8NyST+oU7QTpK7J9zFEFSA0eOmtUNMU3fczlTTTlnCxHmq/jYNFEmkkSG3DDBtW3Y4Q== + +babel-plugin-minify-simplify@^0.5.1: + version "0.5.1" + resolved "https://registry.npmjs.org/babel-plugin-minify-simplify/-/babel-plugin-minify-simplify-0.5.1.tgz#f21613c8b95af3450a2ca71502fdbd91793c8d6a" + integrity sha512-OSYDSnoCxP2cYDMk9gxNAed6uJDiDz65zgL6h8d3tm8qXIagWGMLWhqysT6DY3Vs7Fgq7YUDcjOomhVUb+xX6A== + dependencies: + babel-helper-evaluate-path "^0.5.0" + babel-helper-flip-expressions "^0.4.3" + babel-helper-is-nodes-equiv "^0.0.1" + babel-helper-to-multiple-sequence-expressions "^0.5.0" + +babel-plugin-minify-type-constructors@^0.4.3: + version "0.4.3" + resolved "https://registry.npmjs.org/babel-plugin-minify-type-constructors/-/babel-plugin-minify-type-constructors-0.4.3.tgz#1bc6f15b87f7ab1085d42b330b717657a2156500" + integrity sha1-G8bxW4f3qxCF1CszC3F2V6IVZQA= + dependencies: + babel-helper-is-void-0 "^0.4.3" + +babel-plugin-named-asset-import@^0.3.1, babel-plugin-named-asset-import@^0.3.6: version "0.3.6" resolved "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.6.tgz#c9750a1b38d85112c9e166bf3ef7c5dbc605f4be" integrity sha512-1aGDUfL1qOOIoqk9QKGIo2lANk+C7ko/fqH0uIyC71x3PEGz0uVP8ISgfEsFuG+FKmjHTvFK/nNM8dowpmUxLA== +babel-plugin-react-docgen@^4.0.0: + version "4.1.0" + resolved "https://registry.npmjs.org/babel-plugin-react-docgen/-/babel-plugin-react-docgen-4.1.0.tgz#1dfa447dac9ca32d625a123df5733a9e47287c26" + integrity sha512-vzpnBlfGv8XOhJM2zbPyyqw2OLEbelgZZsaaRRTpVwNKuYuc+pUg4+dy7i9gCRms0uOQn4osX571HRcCJMJCmA== + dependencies: + lodash "^4.17.15" + react-docgen "^5.0.0" + recast "^0.14.7" + +babel-plugin-syntax-jsx@^6.18.0: + version "6.18.0" + resolved "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" + integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY= + babel-plugin-syntax-object-rest-spread@^6.8.0: version "6.13.0" resolved "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5" integrity sha1-/WU28rzhODb/o6VFjEkDpZe7O/U= +babel-plugin-transform-inline-consecutive-adds@^0.4.3: + version "0.4.3" + resolved "https://registry.npmjs.org/babel-plugin-transform-inline-consecutive-adds/-/babel-plugin-transform-inline-consecutive-adds-0.4.3.tgz#323d47a3ea63a83a7ac3c811ae8e6941faf2b0d1" + integrity sha1-Mj1Ho+pjqDp6w8gRro5pQfrysNE= + +babel-plugin-transform-member-expression-literals@^6.9.4: + version "6.9.4" + resolved "https://registry.npmjs.org/babel-plugin-transform-member-expression-literals/-/babel-plugin-transform-member-expression-literals-6.9.4.tgz#37039c9a0c3313a39495faac2ff3a6b5b9d038bf" + integrity sha1-NwOcmgwzE6OUlfqsL/OmtbnQOL8= + +babel-plugin-transform-merge-sibling-variables@^6.9.4: + version "6.9.4" + resolved "https://registry.npmjs.org/babel-plugin-transform-merge-sibling-variables/-/babel-plugin-transform-merge-sibling-variables-6.9.4.tgz#85b422fc3377b449c9d1cde44087203532401dae" + integrity sha1-hbQi/DN3tEnJ0c3kQIcgNTJAHa4= + +babel-plugin-transform-minify-booleans@^6.9.4: + version "6.9.4" + resolved "https://registry.npmjs.org/babel-plugin-transform-minify-booleans/-/babel-plugin-transform-minify-booleans-6.9.4.tgz#acbb3e56a3555dd23928e4b582d285162dd2b198" + integrity sha1-rLs+VqNVXdI5KOS1gtKFFi3SsZg= + babel-plugin-transform-object-rest-spread@^6.26.0: version "6.26.0" resolved "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz#0f36692d50fef6b7e2d4b3ac1478137a963b7b06" @@ -4212,11 +5106,50 @@ babel-plugin-transform-object-rest-spread@^6.26.0: babel-plugin-syntax-object-rest-spread "^6.8.0" babel-runtime "^6.26.0" +babel-plugin-transform-property-literals@^6.9.4: + version "6.9.4" + resolved "https://registry.npmjs.org/babel-plugin-transform-property-literals/-/babel-plugin-transform-property-literals-6.9.4.tgz#98c1d21e255736573f93ece54459f6ce24985d39" + integrity sha1-mMHSHiVXNlc/k+zlRFn2ziSYXTk= + dependencies: + esutils "^2.0.2" + babel-plugin-transform-react-remove-prop-types@0.4.24: version "0.4.24" resolved "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz#f2edaf9b4c6a5fbe5c1d678bfb531078c1555f3a" integrity sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA== +babel-plugin-transform-regexp-constructors@^0.4.3: + version "0.4.3" + resolved "https://registry.npmjs.org/babel-plugin-transform-regexp-constructors/-/babel-plugin-transform-regexp-constructors-0.4.3.tgz#58b7775b63afcf33328fae9a5f88fbd4fb0b4965" + integrity sha1-WLd3W2OvzzMyj66aX4j71PsLSWU= + +babel-plugin-transform-remove-console@^6.9.4: + version "6.9.4" + resolved "https://registry.npmjs.org/babel-plugin-transform-remove-console/-/babel-plugin-transform-remove-console-6.9.4.tgz#b980360c067384e24b357a588d807d3c83527780" + integrity sha1-uYA2DAZzhOJLNXpYjYB9PINSd4A= + +babel-plugin-transform-remove-debugger@^6.9.4: + version "6.9.4" + resolved "https://registry.npmjs.org/babel-plugin-transform-remove-debugger/-/babel-plugin-transform-remove-debugger-6.9.4.tgz#42b727631c97978e1eb2d199a7aec84a18339ef2" + integrity sha1-QrcnYxyXl44estGZp67IShgznvI= + +babel-plugin-transform-remove-undefined@^0.5.0: + version "0.5.0" + resolved "https://registry.npmjs.org/babel-plugin-transform-remove-undefined/-/babel-plugin-transform-remove-undefined-0.5.0.tgz#80208b31225766c630c97fa2d288952056ea22dd" + integrity sha512-+M7fJYFaEE/M9CXa0/IRkDbiV3wRELzA1kKQFCJ4ifhrzLKn/9VCCgj9OFmYWwBd8IB48YdgPkHYtbYq+4vtHQ== + dependencies: + babel-helper-evaluate-path "^0.5.0" + +babel-plugin-transform-simplify-comparison-operators@^6.9.4: + version "6.9.4" + resolved "https://registry.npmjs.org/babel-plugin-transform-simplify-comparison-operators/-/babel-plugin-transform-simplify-comparison-operators-6.9.4.tgz#f62afe096cab0e1f68a2d753fdf283888471ceb9" + integrity sha1-9ir+CWyrDh9ootdT/fKDiIRxzrk= + +babel-plugin-transform-undefined-to-void@^6.9.4: + version "6.9.4" + resolved "https://registry.npmjs.org/babel-plugin-transform-undefined-to-void/-/babel-plugin-transform-undefined-to-void-6.9.4.tgz#be241ca81404030678b748717322b89d0c8fe280" + integrity sha1-viQcqBQEAwZ4t0hxcyK4nQyP4oA= + babel-polyfill@6.26.0: version "6.26.0" resolved "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz#379937abc67d7895970adc621f284cd966cf2153" @@ -4243,6 +5176,35 @@ babel-preset-jest@^25.1.0: "@babel/plugin-syntax-object-rest-spread" "^7.0.0" babel-plugin-jest-hoist "^25.1.0" +"babel-preset-minify@^0.5.0 || 0.6.0-alpha.5": + version "0.5.1" + resolved "https://registry.npmjs.org/babel-preset-minify/-/babel-preset-minify-0.5.1.tgz#25f5d0bce36ec818be80338d0e594106e21eaa9f" + integrity sha512-1IajDumYOAPYImkHbrKeiN5AKKP9iOmRoO2IPbIuVp0j2iuCcj0n7P260z38siKMZZ+85d3mJZdtW8IgOv+Tzg== + dependencies: + babel-plugin-minify-builtins "^0.5.0" + babel-plugin-minify-constant-folding "^0.5.0" + babel-plugin-minify-dead-code-elimination "^0.5.1" + babel-plugin-minify-flip-comparisons "^0.4.3" + babel-plugin-minify-guarded-expressions "^0.4.4" + babel-plugin-minify-infinity "^0.4.3" + babel-plugin-minify-mangle-names "^0.5.0" + babel-plugin-minify-numeric-literals "^0.4.3" + babel-plugin-minify-replace "^0.5.0" + babel-plugin-minify-simplify "^0.5.1" + babel-plugin-minify-type-constructors "^0.4.3" + babel-plugin-transform-inline-consecutive-adds "^0.4.3" + babel-plugin-transform-member-expression-literals "^6.9.4" + babel-plugin-transform-merge-sibling-variables "^6.9.4" + babel-plugin-transform-minify-booleans "^6.9.4" + babel-plugin-transform-property-literals "^6.9.4" + babel-plugin-transform-regexp-constructors "^0.4.3" + babel-plugin-transform-remove-console "^6.9.4" + babel-plugin-transform-remove-debugger "^6.9.4" + babel-plugin-transform-remove-undefined "^0.5.0" + babel-plugin-transform-simplify-comparison-operators "^6.9.4" + babel-plugin-transform-undefined-to-void "^6.9.4" + lodash "^4.17.11" + babel-preset-react-app@^9.1.1: version "9.1.1" resolved "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-9.1.1.tgz#d1ceb47cbe48b285fdd5c562c54c432ed5a41e0e" @@ -4298,6 +5260,11 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" +batch-processor@1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/batch-processor/-/batch-processor-1.0.0.tgz#75c95c32b748e0850d10c2b168f6bdbe9891ace8" + integrity sha1-dclcMrdI4IUNEMKxaPa9vpiRrOg= + batch@0.6.1: version "0.6.1" resolved "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" @@ -4354,7 +5321,7 @@ bindings@^1.5.0: dependencies: file-uri-to-path "1.0.0" -bluebird@^3.5.1, bluebird@^3.5.3, bluebird@^3.5.5: +bluebird@^3.3.5, bluebird@^3.5.1, bluebird@^3.5.3, bluebird@^3.5.5: version "3.7.2" resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== @@ -4420,6 +5387,20 @@ boxen@^1.2.1: term-size "^1.2.0" widest-line "^2.0.0" +boxen@^4.1.0: + version "4.2.0" + resolved "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64" + integrity sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ== + dependencies: + ansi-align "^3.0.0" + camelcase "^5.3.1" + chalk "^3.0.0" + cli-boxes "^2.2.0" + string-width "^4.1.0" + term-size "^2.1.0" + type-fest "^0.8.1" + widest-line "^3.1.0" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -4527,6 +5508,15 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" +browserslist@4.7.0: + version "4.7.0" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.7.0.tgz#9ee89225ffc07db03409f2fee524dc8227458a17" + integrity sha512-9rGNDtnj+HaahxiVV38Gn8n8Lr8REKsel68v1sPFfIGEK6uSXTY3h9acgiT1dZVtOOUtifo/Dn8daDQ5dUgVsA== + dependencies: + caniuse-lite "^1.0.30000989" + electron-to-chromium "^1.3.247" + node-releases "^1.1.29" + browserslist@4.8.6: version "4.8.6" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.8.6.tgz#96406f3f5f0755d272e27a66f4163ca821590a7e" @@ -4770,6 +5760,11 @@ camelcase@^4.0.0, camelcase@^4.1.0: resolved "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= +can-use-dom@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/can-use-dom/-/can-use-dom-0.1.0.tgz#22cc4a34a0abc43950f42c6411024a3f6366b45a" + integrity sha1-IsxKNKCrxDlQ9CxkEQJKP2NmtFo= + caniuse-api@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" @@ -4785,6 +5780,11 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001020, can resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001030.tgz#78076c4c6d67d3e41d6eb9399853fb27fe6e44ee" integrity sha512-QGK0W4Ft/Ac+zTjEiRJfwDNATvS3fodDczBXrH42784kcfqcDKpEPfN08N0HQjrAp8He/Jw8QiSS9QRn7XAbUw== +caniuse-lite@^1.0.30000989: + version "1.0.30001035" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001035.tgz#2bb53b8aa4716b2ed08e088d4dc816a5fe089a1e" + integrity sha512-C1ZxgkuA4/bUEdMbU5WrGY4+UhMFFiXrgNAfxiMIqWgFTWfv/xsZCS2xEHT2LMq7xAZfuAnu6mcqyDl0ZR6wLQ== + capture-exit@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" @@ -4805,7 +5805,7 @@ cardinal@^2.1.1: ansicolors "~0.3.2" redeyed "~2.1.0" -case-sensitive-paths-webpack-plugin@2.3.0: +case-sensitive-paths-webpack-plugin@2.3.0, case-sensitive-paths-webpack-plugin@^2.2.0: version "2.3.0" resolved "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.3.0.tgz#23ac613cc9a856e4f88ff8bb73bbb5e989825cf7" integrity sha512-/4YgnZS8y1UXXmC02xD5rRrBEu6T5ub+mQHLNRj0fzTRbgdBYhsNo2V5EqwgqrExjxsjtF/OpAKAMkKsxbD5XQ== @@ -4848,12 +5848,27 @@ change-emitter@^0.1.2: resolved "https://registry.npmjs.org/change-emitter/-/change-emitter-0.1.6.tgz#e8b2fe3d7f1ab7d69a32199aff91ea6931409515" integrity sha1-6LL+PX8at9aaMhma/5HqaTFAlRU= +character-entities-legacy@^1.0.0: + version "1.1.4" + resolved "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" + integrity sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA== + +character-entities@^1.0.0: + version "1.2.4" + resolved "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz#e12c3939b7eaf4e5b15e7ad4c5e28e1d48c5b16b" + integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw== + +character-reference-invalid@^1.0.0: + version "1.1.4" + resolved "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" + integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== + chardet@^0.7.0: version "0.7.0" resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -chokidar@^2.0.2, chokidar@^2.1.8: +chokidar@^2.0.2, chokidar@^2.0.4, chokidar@^2.1.8: version "2.1.8" resolved "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== @@ -4934,7 +5949,7 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -classnames@^2.2.6: +classnames@^2.2.5, classnames@^2.2.6: version "2.2.6" resolved "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== @@ -4956,6 +5971,11 @@ cli-boxes@^1.0.0: resolved "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" integrity sha1-T6kXw+WclKAEzWH47lCdplFocUM= +cli-boxes@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.0.tgz#538ecae8f9c6ca508e3c3c95b453fe93cb4c168d" + integrity sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w== + cli-columns@^3.1.2: version "3.1.2" resolved "https://registry.npmjs.org/cli-columns/-/cli-columns-3.1.2.tgz#6732d972979efc2ae444a1f08e08fa139c96a18e" @@ -4983,7 +6003,7 @@ cli-spinners@^2.2.0: resolved "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.2.0.tgz#e8b988d9206c692302d8ee834e7a85c0144d8f77" integrity sha512-tgU3fKwzYjiLEQgPMD9Jt+JjHVL9kW93FiIMX/l7rivvOD4/LL0Mf7gda3+4U2KJBloybwgj5KEoQgGRioMiKQ== -cli-table3@^0.5.0, cli-table3@^0.5.1: +cli-table3@0.5.1, cli-table3@^0.5.0, cli-table3@^0.5.1: version "0.5.1" resolved "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202" integrity sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw== @@ -5013,6 +6033,15 @@ cli-width@^2.0.0: resolved "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= +clipboard@^2.0.0: + version "2.0.6" + resolved "https://registry.npmjs.org/clipboard/-/clipboard-2.0.6.tgz#52921296eec0fdf77ead1749421b21c968647376" + integrity sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg== + dependencies: + good-listener "^1.2.2" + select "^1.1.2" + tiny-emitter "^2.0.0" + cliui@^3.2.0: version "3.2.0" resolved "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" @@ -5184,12 +6213,17 @@ combined-stream@^1.0.6, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" +comma-separated-tokens@^1.0.0: + version "1.0.8" + resolved "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" + integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== + commander@2.17.x: version "2.17.1" resolved "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== -commander@^2.11.0, commander@^2.20.0, commander@~2.20.3: +commander@^2.11.0, commander@^2.19.0, commander@^2.20.0, commander@~2.20.3: version "2.20.3" resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -5472,7 +6506,7 @@ conventional-recommended-bump@^5.0.0: meow "^4.0.0" q "^1.5.1" -convert-source-map@1.7.0, convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: +convert-source-map@1.7.0, convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== @@ -5511,7 +6545,7 @@ copy-descriptor@^0.1.0: resolved "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= -copy-to-clipboard@^3.2.0: +copy-to-clipboard@^3.0.8, copy-to-clipboard@^3.2.0: version "3.3.1" resolved "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz#115aa1a9998ffab6196f93076ad6da3b913662ae" integrity sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw== @@ -5526,6 +6560,11 @@ core-js-compat@^3.6.2: browserslist "^4.8.3" semver "7.0.0" +core-js-pure@^3.0.1: + version "3.6.4" + resolved "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.4.tgz#4bf1ba866e25814f149d4e9aaa08c36173506e3a" + integrity sha512-epIhRLkXdgv32xIUFaaAry2wdxZYBi6bgM7cB136dzzXXa+dFyRLTZeLUJxnd8ShrmyVXBub63n2NHo2JAt8Cw== + core-js@^1.0.0: version "1.2.7" resolved "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" @@ -5536,7 +6575,7 @@ core-js@^2.4.0, core-js@^2.5.0: resolved "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c" integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== -core-js@^3.5.0: +core-js@^3.0.1, core-js@^3.0.4, core-js@^3.5.0: version "3.6.4" resolved "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz#440a83536b458114b9cb2ac1580ba377dc470647" integrity sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw== @@ -5546,6 +6585,14 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= +corejs-upgrade-webpack-plugin@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/corejs-upgrade-webpack-plugin/-/corejs-upgrade-webpack-plugin-2.2.0.tgz#503293bf1fdcb104918eb40d0294e4776ad6923a" + integrity sha512-J0QMp9GNoiw91Kj/dkIQFZeiCXgXoja/Wlht1SPybxerBWh4NCmb0pOgCv61lrlQZETwvVVfAFAA3IqoEO9aqQ== + dependencies: + resolve-from "^5.0.0" + webpack "^4.38.0" + cosmiconfig@^5.0.0, cosmiconfig@^5.1.0, cosmiconfig@^5.2.0, cosmiconfig@^5.2.1: version "5.2.1" resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" @@ -5605,6 +6652,14 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" +create-react-context@0.3.0, create-react-context@^0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/create-react-context/-/create-react-context-0.3.0.tgz#546dede9dc422def0d3fc2fe03afe0bc0f4f7d8c" + integrity sha512-dNldIoSuNSvlTJ7slIKC/ZFGKexBMBrrcc+TTe1NdmROnaASuLPvqpwj9v4XS4uXZ8+YPu0sNmShX2rXI5LNsw== + dependencies: + gud "^1.0.0" + warning "^4.0.3" + cross-env@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/cross-env/-/cross-env-7.0.0.tgz#5a3b2ddce51ec713ea58f2fb79ce22e65b4f5479" @@ -5619,6 +6674,17 @@ cross-spawn-promise@^0.10.1: dependencies: cross-spawn "^5.1.0" +cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + cross-spawn@7.0.1, cross-spawn@^7.0.0, cross-spawn@^7.0.1: version "7.0.1" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14" @@ -5637,17 +6703,6 @@ cross-spawn@^5.0.1, cross-spawn@^5.1.0: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^6.0.0, cross-spawn@^6.0.5: - version "6.0.5" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - crypto-browserify@^3.11.0: version "3.12.0" resolved "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" @@ -5711,7 +6766,7 @@ css-in-js-utils@^2.0.0: hyphenate-style-name "^1.0.2" isobject "^3.0.1" -css-loader@3.4.2: +css-loader@3.4.2, css-loader@^3.0.0: version "3.4.2" resolved "https://registry.npmjs.org/css-loader/-/css-loader-3.4.2.tgz#d3fdb3358b43f233b78501c5ed7b1c6da6133202" integrity sha512-jYq4zdZT0oS0Iykt+fqnzVLRIeiPWhka+7BqPn+oSIpWJAHak5tmB/WZrJ2a21JhCeFyNnnlroSl8c+MtVndzA== @@ -5924,7 +6979,7 @@ cssstyle@^2.0.0: dependencies: cssom "~0.3.6" -csstype@^2.2.0, csstype@^2.5.2, csstype@^2.5.5, csstype@^2.6.5, csstype@^2.6.7: +csstype@^2.2.0, csstype@^2.5.2, csstype@^2.5.5, csstype@^2.5.7, csstype@^2.6.5, csstype@^2.6.7: version "2.6.9" resolved "https://registry.npmjs.org/csstype/-/csstype-2.6.9.tgz#05141d0cd557a56b8891394c1911c40c8a98d098" integrity sha512-xz39Sb4+OaTsULgUERcCk+TJj8ylkL4aSVDQiX/ksxbELSqwkgt4d4RD7fovIdgJGSuNYqwZEiVjYY5l0ask+Q== @@ -6077,7 +7132,7 @@ dedent@0.7.0, dedent@^0.7.0: resolved "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= -deep-equal@^1.0.1: +deep-equal@^1.0.1, deep-equal@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== @@ -6099,6 +7154,11 @@ deep-is@~0.1.3: resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= +deep-object-diff@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/deep-object-diff/-/deep-object-diff-1.1.0.tgz#d6fabf476c2ed1751fc94d5ca693d2ed8c18bc5a" + integrity sha512-b+QLs5vHgS+IoSNcUE4n9HP2NwcHj7aqnJWsjPtuG75Rh5TOaGt0OjAYInh77d5T16V5cRDC+Pw/6ZZZiETBGw== + default-gateway@^4.2.0: version "4.2.0" resolved "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" @@ -6175,6 +7235,11 @@ delayed-stream@~1.0.0: resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= +delegate@^3.1.2: + version "3.2.0" + resolved "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166" + integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw== + delegates@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" @@ -6241,6 +7306,14 @@ detect-port-alt@1.1.6: address "^1.0.1" debug "^2.6.0" +detect-port@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/detect-port/-/detect-port-1.3.0.tgz#d9c40e9accadd4df5cac6a782aefd014d573d1f1" + integrity sha512-E+B1gzkl2gqxt1IhUzwjrxBKRqx1UzC3WLONHinn8S3T6lwV/agVCyitiFOsGJ/eYuEUBvD71MZHy3Pv1G9doQ== + dependencies: + address "^1.0.1" + debug "^2.6.0" + dezalgo@^1.0.0, dezalgo@~1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" @@ -6360,6 +7433,11 @@ dom-serializer@0: domelementtype "^2.0.1" entities "^2.0.0" +dom-walk@^0.1.0: + version "0.1.1" + resolved "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018" + integrity sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg= + domain-browser@^1.1.1: version "1.2.0" resolved "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" @@ -6434,12 +7512,26 @@ dot-prop@^5.2.0: dependencies: is-obj "^2.0.0" -dotenv-expand@5.1.0: +dotenv-defaults@^1.0.2: + version "1.1.1" + resolved "https://registry.npmjs.org/dotenv-defaults/-/dotenv-defaults-1.1.1.tgz#032c024f4b5906d9990eb06d722dc74cc60ec1bd" + integrity sha512-6fPRo9o/3MxKvmRZBD3oNFdxODdhJtIy1zcJeUSCs6HCy4tarUpd+G67UTU9tF6OWXeSPqsm4fPAB+2eY9Rt9Q== + dependencies: + dotenv "^6.2.0" + +dotenv-expand@5.1.0, dotenv-expand@^5.1.0: version "5.1.0" resolved "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0" integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA== -dotenv@8.2.0: +dotenv-webpack@^1.7.0: + version "1.7.0" + resolved "https://registry.npmjs.org/dotenv-webpack/-/dotenv-webpack-1.7.0.tgz#4384d8c57ee6f405c296278c14a9f9167856d3a1" + integrity sha512-wwNtOBW/6gLQSkb8p43y0Wts970A3xtNiG/mpwj9MLUhtPCQG6i+/DSXXoNN7fbPCU/vQ7JjwGmgOeGZSSZnsw== + dependencies: + dotenv-defaults "^1.0.2" + +dotenv@8.2.0, dotenv@^8.0.0: version "8.2.0" resolved "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== @@ -6449,6 +7541,11 @@ dotenv@^5.0.1: resolved "https://registry.npmjs.org/dotenv/-/dotenv-5.0.1.tgz#a5317459bd3d79ab88cff6e44057a6a3fbb1fcef" integrity sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow== +dotenv@^6.2.0: + version "6.2.0" + resolved "https://registry.npmjs.org/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064" + integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w== + duplexer2@~0.1.0: version "0.1.4" resolved "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" @@ -6494,6 +7591,16 @@ ee-first@1.1.1: resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= +ejs@^2.7.4: + version "2.7.4" + resolved "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba" + integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== + +electron-to-chromium@^1.3.247: + version "1.3.380" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.380.tgz#1e1f07091b42b54bccd0ad6d3a14f2b73b60dc9d" + integrity sha512-2jhQxJKcjcSpVOQm0NAfuLq8o+130blrcawoumdXT6411xG/xIAOyZodO/y7WTaYlz/NHe3sCCAe/cJLnDsqTw== + electron-to-chromium@^1.3.341, electron-to-chromium@^1.3.361: version "1.3.363" resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.363.tgz#08756873e49446a92e0cee6c3cd9eb3c52043826" @@ -6504,6 +7611,13 @@ elegant-spinner@^1.0.1: resolved "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4= +element-resize-detector@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/element-resize-detector/-/element-resize-detector-1.2.1.tgz#b0305194447a4863155e58f13323a0aef30851d1" + integrity sha512-BdFsPepnQr9fznNPF9nF4vQ457U/ZJXQDSNF1zBe7yaga8v9AdZf3/NElYxFdUh7SitSGt040QygiTo6dtatIw== + dependencies: + batch-processor "1.0.0" + elliptic@^6.0.0: version "6.5.2" resolved "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz#05c5678d7173c049d8ca433552224a495d0e3762" @@ -6537,6 +7651,15 @@ emojis-list@^3.0.0: resolved "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== +emotion-theming@^10.0.19: + version "10.0.27" + resolved "https://registry.npmjs.org/emotion-theming/-/emotion-theming-10.0.27.tgz#1887baaec15199862c89b1b984b79806f2b9ab10" + integrity sha512-MlF1yu/gYh8u+sLUqA0YuA9JX0P4Hb69WlKc/9OLo+WCXuX6sy/KoIa+qJimgmr2dWqnypYKYPX37esjDBbhdw== + dependencies: + "@babel/runtime" "^7.5.5" + "@emotion/weak-memoize" "0.2.5" + hoist-non-react-statics "^3.3.0" + encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -6565,7 +7688,7 @@ enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: memory-fs "^0.5.0" tapable "^1.0.0" -entities@^1.1.1: +entities@^1.1.1, entities@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== @@ -6619,7 +7742,7 @@ error-stack-parser@^2.0.6: dependencies: stackframe "^1.1.1" -es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2: +es-abstract@^1.17.0, es-abstract@^1.17.0-next.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.4: version "1.17.4" resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz#e3aedf19706b20e7c2594c35fc0d57605a79e184" integrity sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ== @@ -6636,6 +7759,24 @@ es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2: string.prototype.trimleft "^2.1.1" string.prototype.trimright "^2.1.1" +es-array-method-boxes-properly@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" + integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== + +es-get-iterator@^1.0.2: + version "1.1.0" + resolved "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz#bb98ad9d6d63b31aacdc8f89d5d0ee57bcb5b4c8" + integrity sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ== + dependencies: + es-abstract "^1.17.4" + has-symbols "^1.0.1" + is-arguments "^1.0.4" + is-map "^2.0.1" + is-set "^2.0.1" + is-string "^1.0.5" + isarray "^2.0.5" + es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -6654,6 +7795,11 @@ es5-ext@^0.10.35, es5-ext@^0.10.50: es6-symbol "~3.1.3" next-tick "~1.0.0" +es5-shim@^4.5.13: + version "4.5.13" + resolved "https://registry.npmjs.org/es5-shim/-/es5-shim-4.5.13.tgz#5d88062de049f8969f83783f4a4884395f21d28b" + integrity sha512-xi6hh6gsvDE0MaW4Vp1lgNEBpVcCXRWfPXj5egDvtgLz4L9MEvNwYEMdJH+JJinWkwa8c3c3o5HduV7dB/e1Hw== + es6-iterator@2.0.3, es6-iterator@~2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" @@ -6675,6 +7821,11 @@ es6-promisify@^5.0.0: dependencies: es6-promise "^4.0.3" +es6-shim@^0.35.5: + version "0.35.5" + resolved "https://registry.npmjs.org/es6-shim/-/es6-shim-0.35.5.tgz#46f59dc0a84a1c5029e8ff1166ca0a902077a9ab" + integrity sha512-E9kK/bjtCQRpN1K28Xh4BlmP8egvZBGJJ+9GtnzOwt7mdqtrjHFuVGr7QJfdjBIKqrlU5duPf3pCBoDrkjVYFg== + es6-symbol@^3.1.1, es6-symbol@~3.1.3: version "3.1.3" resolved "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" @@ -6688,16 +7839,16 @@ escape-html@~1.0.3: resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + escape-string-regexp@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - escodegen@^1.11.0, escodegen@^1.11.1, escodegen@^1.9.1: version "1.14.1" resolved "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz#ba01d0c8278b5e95a9a45350142026659027a457" @@ -7098,7 +8249,7 @@ expect@^25.1.0: jest-message-util "^25.1.0" jest-regex-util "^25.1.0" -express@^4.17.1: +express@^4.17.0, express@^4.17.1: version "4.17.1" resolved "https://registry.npmjs.org/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== @@ -7194,6 +8345,11 @@ extsprintf@^1.2.0: resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= +fast-deep-equal@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" + integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= + fast-deep-equal@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" @@ -7250,6 +8406,13 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" +fault@^1.0.2: + version "1.0.4" + resolved "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz#eafcfc0a6d214fc94601e170df29954a4f842f13" + integrity sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA== + dependencies: + format "^0.2.0" + faye-websocket@^0.10.0: version "0.10.0" resolved "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" @@ -7318,7 +8481,7 @@ file-entry-cache@^5.0.1: dependencies: flat-cache "^2.0.1" -file-loader@4.3.0: +file-loader@4.3.0, file-loader@^4.2.0: version "4.3.0" resolved "https://registry.npmjs.org/file-loader/-/file-loader-4.3.0.tgz#780f040f729b3d18019f20605f723e844b8a58af" integrity sha512-aKrYPYjF1yG3oX0kWRrqrSMfgftm7oJW5M+m4owoldH5C51C0RkIwB++JbRvEW3IU6/ZG5n8UvEcdgwOt2UOWA== @@ -7326,11 +8489,25 @@ file-loader@4.3.0: loader-utils "^1.2.3" schema-utils "^2.5.0" +file-system-cache@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/file-system-cache/-/file-system-cache-1.0.5.tgz#84259b36a2bbb8d3d6eb1021d3132ffe64cfff4f" + integrity sha1-hCWbNqK7uNPW6xAh0xMv/mTP/08= + dependencies: + bluebird "^3.3.5" + fs-extra "^0.30.0" + ramda "^0.21.0" + file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== +filesize@3.6.1: + version "3.6.1" + resolved "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" + integrity sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg== + filesize@6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/filesize/-/filesize-6.0.1.tgz#f850b509909c7c86f7e450ea19006c31c2ed3d2f" @@ -7384,7 +8561,7 @@ find-cache-dir@^2.0.0, find-cache-dir@^2.1.0: make-dir "^2.0.0" pkg-dir "^3.0.0" -find-cache-dir@^3.2.0: +find-cache-dir@^3.0.0, find-cache-dir@^3.2.0: version "3.3.1" resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880" integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ== @@ -7411,6 +8588,13 @@ find-root@1.1.0, find-root@^1.1.0: resolved "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== +find-up@3.0.0, find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + find-up@4.1.0, find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" @@ -7434,13 +8618,6 @@ find-up@^2.0.0, find-up@^2.1.0: dependencies: locate-path "^2.0.0" -find-up@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" - integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== - dependencies: - locate-path "^3.0.0" - find-versions@^3.0.0: version "3.2.0" resolved "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz#10297f98030a786829681690545ef659ed1d254e" @@ -7485,6 +8662,11 @@ flush-write-stream@^1.0.0: inherits "^2.0.3" readable-stream "^2.3.6" +focus-lock@^0.6.6: + version "0.6.6" + resolved "https://registry.npmjs.org/focus-lock/-/focus-lock-0.6.6.tgz#98119a755a38cfdbeda0280eaa77e307eee850c7" + integrity sha512-Dx69IXGCq1qsUExWuG+5wkiMqVM/zGx/reXSJSLogECwp3x6KeNQZ+NAetgxEFpnC41rD8U3+jRCW68+LNzdtw== + follow-redirects@^1.0.0: version "1.10.0" resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.10.0.tgz#01f5263aee921c6a54fb91667f08f4155ce169eb" @@ -7514,6 +8696,20 @@ forever-agent@~0.6.1: resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= +fork-ts-checker-webpack-plugin@1.5.0: + version "1.5.0" + resolved "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-1.5.0.tgz#ce1d77190b44d81a761b10b6284a373795e41f0c" + integrity sha512-zEhg7Hz+KhZlBhILYpXy+Beu96gwvkROWJiTXOCyOOMMrdBIRPvsBpBqgTI4jfJGrJXcqGwJR8zsBGDmzY0jsA== + dependencies: + babel-code-frame "^6.22.0" + chalk "^2.4.1" + chokidar "^2.0.4" + micromatch "^3.1.10" + minimatch "^3.0.4" + semver "^5.6.0" + tapable "^1.0.0" + worker-rpc "^0.1.0" + fork-ts-checker-webpack-plugin@3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-3.1.1.tgz#a1642c0d3e65f50c2cc1742e9c0a80f441f86b19" @@ -7550,6 +8746,11 @@ form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" +format@^0.2.0: + version "0.2.2" + resolved "https://registry.npmjs.org/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b" + integrity sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs= + forwarded@~0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" @@ -7583,7 +8784,7 @@ from2@^2.1.0, from2@^2.3.0: inherits "^2.0.1" readable-stream "^2.0.0" -fs-extra@8.1.0, fs-extra@^8.0.0, fs-extra@^8.1.0: +fs-extra@8.1.0, fs-extra@^8.0.0, fs-extra@^8.0.1, fs-extra@^8.1.0: version "8.1.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== @@ -7592,6 +8793,17 @@ fs-extra@8.1.0, fs-extra@^8.0.0, fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" +fs-extra@^0.30.0: + version "0.30.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" + integrity sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A= + dependencies: + graceful-fs "^4.1.2" + jsonfile "^2.1.0" + klaw "^1.0.0" + path-is-absolute "^1.0.0" + rimraf "^2.2.8" + fs-extra@^4.0.2: version "4.0.3" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" @@ -7666,11 +8878,30 @@ function-bind@^1.1.1: resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function.prototype.name@^1.1.0: + version "1.1.2" + resolved "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.2.tgz#5cdf79d7c05db401591dfde83e3b70c5123e9a45" + integrity sha512-C8A+LlHBJjB2AdcRPorc5JvJ5VUoWlXdEHLOJdCI7kjHEtGTpHQUiqMvCIKUwIsGwZX2jZJy761AXsn356bJQg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + functions-have-names "^1.2.0" + functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +functions-have-names@^1.2.0: + version "1.2.1" + resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.1.tgz#a981ac397fa0c9964551402cdc5533d7a4d52f91" + integrity sha512-j48B/ZI7VKs3sgeI2cZp7WXWmZXu7Iq5pl5/vptV5N2mq+DGFuS/ulaDjtaoLpYzuD6u8UgrUKHfgo7fDTSiBA== + +fuse.js@^3.4.6: + version "3.6.1" + resolved "https://registry.npmjs.org/fuse.js/-/fuse.js-3.6.1.tgz#7de85fdd6e1b3377c23ce010892656385fd9b10c" + integrity sha512-hT9yh/tiinkmirKrlv4KWOjztdoZo1mx9Qh4KvWqC7isoXwdUY3PNWUxceF4/qO9R6riA2C29jdTOeQOIROjgw== + gauge@~2.7.3: version "2.7.4" resolved "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" @@ -7861,6 +9092,21 @@ gitconfiglocal@^1.0.0: dependencies: ini "^1.3.2" +glob-base@^0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" + integrity sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q= + dependencies: + glob-parent "^2.0.0" + is-glob "^2.0.0" + +glob-parent@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg= + dependencies: + is-glob "^2.0.0" + glob-parent@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" @@ -7948,6 +9194,14 @@ global-prefix@^3.0.0: kind-of "^6.0.2" which "^1.3.1" +global@^4.3.2, global@^4.4.0: + version "4.4.0" + resolved "https://registry.npmjs.org/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" + integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== + dependencies: + min-document "^2.19.0" + process "^0.11.10" + globals@^11.1.0: version "11.12.0" resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -7960,6 +9214,13 @@ globals@^12.1.0: dependencies: type-fest "^0.8.1" +globalthis@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/globalthis/-/globalthis-1.0.1.tgz#40116f5d9c071f9e8fb0037654df1ab3a83b7ef9" + integrity sha512-mJPRTc/P39NH/iNG4mXa9aIhNymaQikTrnspeCa2ZuJ+mH2QN/rXwtX3XwKrHqWgUQFbNZKtHM105aHzJalElw== + dependencies: + define-properties "^1.1.3" + globby@8.0.2: version "8.0.2" resolved "https://registry.npmjs.org/globby/-/globby-8.0.2.tgz#5697619ccd95c5275dbb2d6faa42087c1a941d8d" @@ -8024,6 +9285,13 @@ globby@^9.2.0: pify "^4.0.1" slash "^2.0.0" +good-listener@^1.2.2: + version "1.2.2" + resolved "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50" + integrity sha1-1TswzfkxPf+33JoNR3CWqm0UXFA= + dependencies: + delegate "^3.1.2" + got@^6.7.1: version "6.7.1" resolved "https://registry.npmjs.org/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" @@ -8041,7 +9309,7 @@ got@^6.7.1: unzip-response "^2.0.1" url-parse-lax "^1.0.0" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3: +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3: version "4.2.3" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== @@ -8179,6 +9447,21 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" +hast-util-parse-selector@^2.0.0: + version "2.2.4" + resolved "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.4.tgz#60c99d0b519e12ab4ed32e58f150ec3f61ed1974" + integrity sha512-gW3sxfynIvZApL4L07wryYF4+C9VvH3AUi7LAnVXV4MneGEgwOByXvFo18BgmTWnm7oHAe874jKbIB1YhHSIzA== + +hastscript@^5.0.0: + version "5.1.2" + resolved "https://registry.npmjs.org/hastscript/-/hastscript-5.1.2.tgz#bde2c2e56d04c62dd24e8c5df288d050a355fb8a" + integrity sha512-WlztFuK+Lrvi3EggsqOkQ52rKbxkXL3RwB6t5lwoa8QLMemoWfBuL43eDrwOamJyR7uKQKdmKYaBH1NZBiIRrQ== + dependencies: + comma-separated-tokens "^1.0.0" + hast-util-parse-selector "^2.0.0" + property-information "^5.0.0" + space-separated-tokens "^1.0.0" + he@1.2.x, he@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" @@ -8189,6 +9472,11 @@ hex-color-regex@^1.1.0: resolved "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== +highlight.js@~9.13.0: + version "9.13.1" + resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-9.13.1.tgz#054586d53a6863311168488a0f58d6c505ce641e" + integrity sha512-Sc28JNQNDzaH6PORtRLMvif9RSn1mYuOoX3omVjnb0+HbpPygU2ALBI0R/wsiqCb4/fcp07Gdo8g+fhtFrQl6A== + history@^4.9.0: version "4.10.1" resolved "https://registry.npmjs.org/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" @@ -8215,7 +9503,7 @@ hoist-non-react-statics@^2.3.1: resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47" integrity sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw== -hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.2.1, hoist-non-react-statics@^3.3.2: +hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.2.1, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -8278,7 +9566,7 @@ html-encoding-sniffer@^1.0.2: dependencies: whatwg-encoding "^1.0.1" -html-entities@^1.2.1: +html-entities@^1.2.0, html-entities@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" integrity sha1-DfKTUfByEWNRXfueVUPl9u7VFi8= @@ -8339,6 +9627,18 @@ html-webpack-plugin@^3.2.0: toposort "^1.0.0" util.promisify "1.0.0" +html-webpack-plugin@^4.0.0-beta.2: + version "4.0.0-beta.14" + resolved "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.0.0-beta.14.tgz#1d3dc299a89391c6fb84c6ed94e7ebe53a9261a0" + integrity sha512-gdThA8oCH7PbHMbezNW5LD+hjxXTzwhfi4XFDKKo5Gs0BR1IhVChEZN1ub4YEKQBmOYasdnCGGPEOFggdm9hNA== + dependencies: + html-minifier-terser "^5.0.1" + loader-utils "^1.2.3" + lodash "^4.17.15" + pretty-error "^2.1.1" + tapable "^1.1.3" + util.promisify "1.0.0" + htmlparser2@^3.3.0: version "3.10.1" resolved "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" @@ -8768,6 +10068,11 @@ interpret@^1.0.0: resolved "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296" integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw== +interpret@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/interpret/-/interpret-2.0.0.tgz#b783ffac0b8371503e9ab39561df223286aa5433" + integrity sha512-e0/LknJ8wpMMhTiWcjivB+ESwIuvHnBSlBbmP/pSb8CQJldoj1p2qv7xGZ/+BtbTziYRFSz8OsvdbiX45LtYQA== + into-stream@^5.0.0: version "5.1.1" resolved "https://registry.npmjs.org/into-stream/-/into-stream-5.1.1.tgz#f9a20a348a11f3c13face22763f2d02e127f4db8" @@ -8776,7 +10081,7 @@ into-stream@^5.0.0: from2 "^2.3.0" p-is-promise "^3.0.0" -invariant@^2.2.2, invariant@^2.2.4: +invariant@^2.2.2, invariant@^2.2.3, invariant@^2.2.4: version "2.2.4" resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== @@ -8832,6 +10137,19 @@ is-accessor-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" +is-alphabetical@^1.0.0: + version "1.0.4" + resolved "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" + integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== + +is-alphanumerical@^1.0.0: + version "1.0.4" + resolved "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf" + integrity sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A== + dependencies: + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + is-arguments@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" @@ -8923,6 +10241,11 @@ is-date-object@^1.0.1: resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== +is-decimal@^1.0.0: + version "1.0.4" + resolved "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" + integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== + is-descriptor@^0.1.0: version "0.1.6" resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" @@ -8951,6 +10274,14 @@ is-docker@^2.0.0: resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz#2cb0df0e75e2d064fe1864c37cdeacb7b2dcf25b" integrity sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ== +is-dom@^1.0.9: + version "1.1.0" + resolved "https://registry.npmjs.org/is-dom/-/is-dom-1.1.0.tgz#af1fced292742443bb59ca3f76ab5e80907b4e8a" + integrity sha512-u82f6mvhYxRPKpw8V1N0W8ce1xXwOrQtgGcxl6UCL5zBmZu3is/18K0rR7uFCnMDuAsS/3W54mGL4vsaFUQlEQ== + dependencies: + is-object "^1.0.1" + is-window "^1.0.2" + is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" @@ -8963,6 +10294,11 @@ is-extendable@^1.0.1: dependencies: is-plain-object "^2.0.4" +is-extglob@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA= + is-extglob@^2.1.0, is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -8990,11 +10326,23 @@ is-fullwidth-code-point@^3.0.0: resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== +is-function@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5" + integrity sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU= + is-generator-fn@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== +is-glob@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + integrity sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM= + dependencies: + is-extglob "^1.0.0" + is-glob@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" @@ -9009,6 +10357,11 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-hexadecimal@^1.0.0: + version "1.0.4" + resolved "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" + integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== + is-in-browser@^1.0.2, is-in-browser@^1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835" @@ -9027,6 +10380,11 @@ is-interactive@^1.0.0: resolved "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== +is-map@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz#520dafc4307bb8ebc33b813de5ce7c9400d644a1" + integrity sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw== + is-npm@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" @@ -9054,6 +10412,11 @@ is-obj@^2.0.0: resolved "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== +is-object@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470" + integrity sha1-iVJojF7C/9awPsyF52ngKQMINHA= + is-observable@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/is-observable/-/is-observable-1.1.0.tgz#b3e986c8f44de950867cab5403f5a3465005975e" @@ -9148,6 +10511,11 @@ is-root@2.1.0: resolved "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c" integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg== +is-set@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz#d1604afdab1724986d30091575f54945da7e5f43" + integrity sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA== + is-ssh@^1.3.0: version "1.3.1" resolved "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.1.tgz#f349a8cadd24e65298037a522cf7520f2e81a0f3" @@ -9165,7 +10533,7 @@ is-stream@^2.0.0: resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== -is-string@^1.0.5: +is-string@^1.0.4, is-string@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== @@ -9177,7 +10545,7 @@ is-svg@^3.0.0: dependencies: html-comment-regex "^1.1.0" -is-symbol@^1.0.2: +is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== @@ -9201,6 +10569,11 @@ is-utf8@^0.2.0, is-utf8@^0.2.1: resolved "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= +is-window@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/is-window/-/is-window-1.0.2.tgz#2c896ca53db97de45d3c33133a65d8c9f563480d" + integrity sha1-LIlspT25feRdPDMTOmXYyfVjSA0= + is-windows@^1.0.0, is-windows@^1.0.1, is-windows@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -9226,6 +10599,11 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -9361,6 +10739,19 @@ istanbul-reports@^3.0.0: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +iterate-iterator@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.1.tgz#1693a768c1ddd79c969051459453f082fe82e9f6" + integrity sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw== + +iterate-value@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz#935115bd37d006a52046535ebc8d07e9c9337f57" + integrity sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ== + dependencies: + es-get-iterator "^1.0.2" + iterate-iterator "^1.0.1" + java-properties@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/java-properties/-/java-properties-1.0.2.tgz#ccd1fa73907438a5b5c38982269d0e771fe78211" @@ -10289,6 +11680,20 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" +json5@^2.1.1, json5@^2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/json5/-/json5-2.1.2.tgz#43ef1f0af9835dd624751a6b7fa48874fb2d608e" + integrity sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ== + dependencies: + minimist "^1.2.5" + +jsonfile@^2.1.0: + version "2.4.0" + resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" + integrity sha1-NzaitCi4e72gzIO1P6PWM6NcKug= + optionalDependencies: + graceful-fs "^4.1.6" + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -10429,6 +11834,13 @@ kind-of@^6.0.0, kind-of@^6.0.2: resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== +klaw@^1.0.0: + version "1.3.1" + resolved "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" + integrity sha1-QIhDO0azsbolnXh4XY6W9zugJDk= + optionalDependencies: + graceful-fs "^4.1.9" + kleur@^3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" @@ -10464,6 +11876,17 @@ lazy-property@~1.0.0: resolved "https://registry.npmjs.org/lazy-property/-/lazy-property-1.0.0.tgz#84ddc4b370679ba8bd4cdcfa4c06b43d57111147" integrity sha1-hN3Es3Bnm6i9TNz6TAa0PVcREUc= +lazy-universal-dotenv@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/lazy-universal-dotenv/-/lazy-universal-dotenv-3.0.1.tgz#a6c8938414bca426ab8c9463940da451a911db38" + integrity sha512-prXSYk799h3GY3iOWnC6ZigYzMPjxN2svgjJ9shk7oMadSNX3wXy0B6F32PMJv7qtMnrIbUxoEHzbutvxR2LBQ== + dependencies: + "@babel/runtime" "^7.5.0" + app-root-dir "^1.0.2" + core-js "^3.0.4" + dotenv "^8.0.0" + dotenv-expand "^5.1.0" + lcid@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" @@ -10803,7 +12226,7 @@ loader-utils@^0.2.16: json5 "^0.5.0" object-assign "^4.0.1" -loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3: +loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== @@ -10884,6 +12307,11 @@ lodash.clonedeep@^4.5.0, lodash.clonedeep@~4.5.0: resolved "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= + lodash.escaperegexp@^4.1.2: version "4.1.2" resolved "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347" @@ -10944,6 +12372,11 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "^3.0.0" +lodash.throttle@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" + integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= + lodash.toarray@^4.4.0: version "4.4.0" resolved "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" @@ -11046,6 +12479,14 @@ lowercase-keys@^1.0.0: resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== +lowlight@~1.11.0: + version "1.11.0" + resolved "https://registry.npmjs.org/lowlight/-/lowlight-1.11.0.tgz#1304d83005126d4e8b1dc0f07981e9b689ec2efc" + integrity sha512-xrGGN6XLL7MbTMdPD6NfWPwY43SNkjf/d0mecSx/CW36fUZTjRHEq0/Cdug3TWKtRXLWi7iMl1eP0olYxj/a4A== + dependencies: + fault "^1.0.2" + highlight.js "~9.13.0" + lru-cache@^4.0.1: version "4.1.5" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" @@ -11144,6 +12585,11 @@ map-obj@^2.0.0: resolved "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz#a65cd29087a92598b8791257a523e021222ac1f9" integrity sha1-plzSkIepJZi4eRJXpSPgISIqwfk= +map-or-similar@^1.5.0: + version "1.5.0" + resolved "https://registry.npmjs.org/map-or-similar/-/map-or-similar-1.5.0.tgz#6de2653174adfb5d9edc33c69d3e92a1b76faf08" + integrity sha1-beJlMXSt+12e3DPGnT6Sobdvrwg= + map-visit@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" @@ -11151,6 +12597,14 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +markdown-to-jsx@^6.9.1, markdown-to-jsx@^6.9.3: + version "6.11.0" + resolved "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-6.11.0.tgz#a2e3f2bc781c3402d8bb0f8e0a12a186474623b0" + integrity sha512-RH7LCJQ4RFmPqVeZEesKaO1biRzB/k4utoofmTCp3Eiw6D7qfvK8fzZq/2bjEJAtVkfPrM5SMt5APGf2rnaKMg== + dependencies: + prop-types "^15.6.2" + unquote "^1.1.0" + marked-terminal@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/marked-terminal/-/marked-terminal-4.0.0.tgz#2c7aa2c0eec496f05cd61f768d80d35db0bf6a86" @@ -11213,6 +12667,13 @@ mem@^4.0.0: mimic-fn "^2.0.0" p-is-promise "^2.0.0" +memoizerific@^1.11.3: + version "1.11.3" + resolved "https://registry.npmjs.org/memoizerific/-/memoizerific-1.11.3.tgz#7c87a4646444c32d75438570905f2dbd1b1a805a" + integrity sha1-fIekZGREwy11Q4VwkF8tvRsagFo= + dependencies: + map-or-similar "^1.5.0" + memory-fs@^0.4.1: version "0.4.1" resolved "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" @@ -11386,6 +12847,13 @@ mimic-fn@^2.0.0, mimic-fn@^2.1.0: resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +min-document@^2.19.0: + version "2.19.0" + resolved "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" + integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU= + dependencies: + dom-walk "^0.1.0" + min-indent@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.0.tgz#cfc45c37e9ec0d8f0a0ec3dd4ef7f7c3abe39256" @@ -11410,6 +12878,16 @@ mini-css-extract-plugin@0.9.0: schema-utils "^1.0.0" webpack-sources "^1.1.0" +mini-css-extract-plugin@^0.7.0: + version "0.7.0" + resolved "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.7.0.tgz#5ba8290fbb4179a43dd27cca444ba150bee743a0" + integrity sha512-RQIw6+7utTYn8DBGsf/LpRgZCJMpZt+kuawJ/fju0KiOL6nAaTBNmCJwS7HtwSCXfS47gCkmtBFS7HdsquhdxQ== + dependencies: + loader-utils "^1.1.0" + normalize-url "1.9.1" + schema-utils "^1.0.0" + webpack-sources "^1.1.0" + minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" @@ -11420,7 +12898,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: resolved "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= -minimatch@3.0.4, minimatch@^3.0.4: +minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -11445,6 +12923,11 @@ minimist@1.2.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0: resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= +minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + minimist@~0.0.1: version "0.0.10" resolved "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" @@ -11544,6 +13027,13 @@ mkdirp@0.5.1, mkdirp@0.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0 dependencies: minimist "0.0.8" +mkdirp@^0.5.3: + version "0.5.3" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz#5a514b7179259287952881e94410ec5465659f8c" + integrity sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg== + dependencies: + minimist "^1.2.5" + modify-values@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" @@ -11704,6 +13194,13 @@ no-case@^3.0.3: lower-case "^2.0.1" tslib "^1.10.0" +node-dir@^0.1.10: + version "0.1.17" + resolved "https://registry.npmjs.org/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" + integrity sha1-X1Zl2TNRM1yqvvjxxVRRbPXx5OU= + dependencies: + minimatch "^3.0.2" + node-emoji@^1.10.0: version "1.10.0" resolved "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz#8886abd25d9c7bb61802a658523d1f8d2a89b2da" @@ -11728,7 +13225,7 @@ node-fetch@^1.0.1: encoding "^0.1.11" is-stream "^1.0.1" -node-fetch@^2.3.0, node-fetch@^2.5.0: +node-fetch@^2.3.0, node-fetch@^2.5.0, node-fetch@^2.6.0: version "2.6.0" resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== @@ -11816,6 +13313,13 @@ node-notifier@^6.0.0: shellwords "^0.1.1" which "^1.3.1" +node-releases@^1.1.29: + version "1.1.52" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-1.1.52.tgz#bcffee3e0a758e92e44ecfaecd0a47554b0bcba9" + integrity sha512-snSiT1UypkgGt2wxPqS6ImEUICbNCMb31yaxWrOLXjhlt2z2/IBpaOxzONExqSm4y5oLnAqjjRWu+wsDzK5yNQ== + dependencies: + semver "^6.3.0" + node-releases@^1.1.47, node-releases@^1.1.50: version "1.1.50" resolved "https://registry.npmjs.org/node-releases/-/node-releases-1.1.50.tgz#803c40d2c45db172d0410e4efec83aa8c6ad0592" @@ -12245,7 +13749,7 @@ object.entries@^1.1.0, object.entries@^1.1.1: function-bind "^1.1.1" has "^1.0.3" -object.fromentries@^2.0.2: +"object.fromentries@^2.0.0 || ^1.0.0", object.fromentries@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.2.tgz#4a09c9b9bb3843dd0f89acdb517a794d4f355ac9" integrity sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ== @@ -12323,7 +13827,14 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" -open@^7.0.2: +open@^6.3.0: + version "6.4.0" + resolved "https://registry.npmjs.org/open/-/open-6.4.0.tgz#5c13e96d0dc894686164f18965ecfe889ecfc8a9" + integrity sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg== + dependencies: + is-wsl "^1.1.0" + +open@^7.0.0, open@^7.0.2: version "7.0.3" resolved "https://registry.npmjs.org/open/-/open-7.0.3.tgz#db551a1af9c7ab4c7af664139930826138531c48" integrity sha512-sP2ru2v0P290WFfv49Ap8MF6PkzGNnGlAwHweB4WR4mr5d2d0woiCluUeJ218w7/+PmoBy9JmYgD5A4mLcWOFA== @@ -12687,6 +14198,18 @@ parse-asn1@^5.0.0: pbkdf2 "^3.0.3" safe-buffer "^5.1.1" +parse-entities@^1.1.2: + version "1.2.2" + resolved "https://registry.npmjs.org/parse-entities/-/parse-entities-1.2.2.tgz#c31bf0f653b6661354f8973559cb86dd1d5edf50" + integrity sha512-NzfpbxW/NPrzZ/yYSoQxyqUZMZXIdCfE0OIN4ESsnptHJECoUk3FZktxNuzQf4tjt5UEopnxpYJbvYuxIFDdsg== + dependencies: + character-entities "^1.0.0" + character-entities-legacy "^1.0.0" + character-reference-invalid "^1.0.0" + is-alphanumerical "^1.0.0" + is-decimal "^1.0.0" + is-hexadecimal "^1.0.0" + parse-github-repo-url@^1.3.0: version "1.4.1" resolved "https://registry.npmjs.org/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz#9e7d8bb252a6cb6ba42595060b7bf6df3dbc1f50" @@ -12951,6 +14474,13 @@ pkg-dir@^4.1.0, pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +pkg-up@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f" + integrity sha1-yBmscoBZpGHKscOImivjxJoATX8= + dependencies: + find-up "^2.1.0" + pkg-up@3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" @@ -12970,6 +14500,13 @@ pn@^1.1.0: resolved "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== +pnp-webpack-plugin@1.5.0: + version "1.5.0" + resolved "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.5.0.tgz#62a1cd3068f46d564bb33c56eb250e4d586676eb" + integrity sha512-jd9olUr9D7do+RN8Wspzhpxhgp1n6Vd0NtQ4SFkmIACZoEL1nkyAdW9Ygrinjec0vgDcWjscFQQ1gDW8rsfKTg== + dependencies: + ts-pnp "^1.1.2" + pnp-webpack-plugin@1.6.0: version "1.6.0" resolved "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.0.tgz#d5c068013a2fdc82224ca50ed179c8fba9036a8e" @@ -12977,7 +14514,14 @@ pnp-webpack-plugin@1.6.0: dependencies: ts-pnp "^1.1.2" -popper.js@^1.14.1: +polished@^3.3.1: + version "3.5.0" + resolved "https://registry.npmjs.org/polished/-/polished-3.5.0.tgz#bffb0db79025c61d1f735f9bb77a379f5fc46345" + integrity sha512-TujkqjczBuuG8ObaNeq+zCCu46tTdaWxqtMxCGTxsV5NYTr9pL08H1P0jgy1V4PXLWm2UlIj+icSEwAZv7I8TA== + dependencies: + "@babel/runtime" "^7.8.7" + +popper.js@^1.14.1, popper.js@^1.14.4, popper.js@^1.14.7: version "1.16.1" resolved "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b" integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ== @@ -13163,6 +14707,13 @@ postcss-flexbugs-fixes@4.1.0: dependencies: postcss "^7.0.0" +postcss-flexbugs-fixes@^4.1.0: + version "4.2.0" + resolved "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-4.2.0.tgz#662b3dcb6354638b9213a55eed8913bcdc8d004a" + integrity sha512-QRE0n3hpkxxS/OGvzOa+PDuy4mh/Jg4o9ui22/ko5iGYOG3M5dfJabjnAZjTdh2G9F85c7Hv8hWcEDEKW/xceQ== + dependencies: + postcss "^7.0.26" + postcss-focus-visible@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-4.0.0.tgz#477d107113ade6024b14128317ade2bd1e17046e" @@ -13224,7 +14775,7 @@ postcss-load-config@^2.0.0: cosmiconfig "^5.0.0" import-cwd "^2.0.0" -postcss-loader@3.0.0: +postcss-loader@3.0.0, postcss-loader@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz#6b97943e47c72d845fa9e03f273773d4e8dd6c2d" integrity sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA== @@ -13705,7 +15256,26 @@ pretty-format@^25.1.0: ansi-styles "^4.0.0" react-is "^16.12.0" -private@^0.1.6: +pretty-hrtime@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" + integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE= + +prismjs@^1.8.4: + version "1.19.0" + resolved "https://registry.npmjs.org/prismjs/-/prismjs-1.19.0.tgz#713afbd45c3baca4b321569f2df39e17e729d4dc" + integrity sha512-IVFtbW9mCWm9eOIaEkNyo2Vl4NnEifis2GQ7/MLRG5TQe6t+4Sj9J5QWI9i3v+SS43uZBlCAOn+zYTVYQcPXJw== + optionalDependencies: + clipboard "^2.0.0" + +prismjs@~1.17.0: + version "1.17.1" + resolved "https://registry.npmjs.org/prismjs/-/prismjs-1.17.1.tgz#e669fcbd4cdd873c35102881c33b14d0d68519be" + integrity sha512-PrEDJAFdUGbOP6xK/UsfkC5ghJsPJviKgnQOoxaDbBjwc8op68Quupwt1DeAFoG8GImPhiKXAvvsH7wDSLsu1Q== + optionalDependencies: + clipboard "^2.0.0" + +private@^0.1.6, private@~0.1.5: version "0.1.8" resolved "https://registry.npmjs.org/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== @@ -13738,6 +15308,26 @@ promise-retry@^1.1.1: err-code "^1.0.0" retry "^0.10.0" +promise.allsettled@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.2.tgz#d66f78fbb600e83e863d893e98b3d4376a9c47c9" + integrity sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg== + dependencies: + array.prototype.map "^1.0.1" + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + iterate-value "^1.0.0" + +promise.prototype.finally@^3.1.0: + version "3.1.2" + resolved "https://registry.npmjs.org/promise.prototype.finally/-/promise.prototype.finally-3.1.2.tgz#b8af89160c9c673cefe3b4c4435b53cfd0287067" + integrity sha512-A2HuJWl2opDH0EafgdjwEw7HysI8ff/n4lW4QEVBCUXFk9QeGecBWv0Deph0UmLe3tTNYegz8MOjsVuE6SMoJA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.0" + function-bind "^1.1.1" + promise@^7.1.1: version "7.3.1" resolved "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" @@ -13767,7 +15357,7 @@ promzard@^0.3.0: dependencies: read "1" -prop-types@^15.5.4, prop-types@^15.5.8, prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@^15.5.4, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -13776,6 +15366,13 @@ prop-types@^15.5.4, prop-types@^15.5.8, prop-types@^15.6.2, prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.8.1" +property-information@^5.0.0: + version "5.4.0" + resolved "https://registry.npmjs.org/property-information/-/property-information-5.4.0.tgz#16e08f13f4e5c4a7be2e4ec431c01c4f8dba869a" + integrity sha512-nmMWAm/3vKFGmmOWOcdLjgq/Hlxa+hsuR/px1Lp/UGEyc5A22A6l78Shc2C0E71sPmAqglni+HrS7L7VJ7AUCA== + dependencies: + xtend "^4.0.0" + proto-list@~1.2.1: version "1.2.4" resolved "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" @@ -13888,6 +15485,11 @@ qs@6.7.0: resolved "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== +qs@^6.6.0: + version "6.9.1" + resolved "https://registry.npmjs.org/qs/-/qs-6.9.1.tgz#20082c65cb78223635ab1a9eaca8875a29bf8ec9" + integrity sha512-Cxm7/SS/y/Z3MHWSxXb8lIFqgqBowP5JMlTUFyJN88y0SGQhVmZnqFK/PeuMX9LzUyWsqqhNxIyg0jlzq946yA== + qs@~6.5.2: version "6.5.2" resolved "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" @@ -13915,7 +15517,7 @@ querystring-es3@^0.2.0: resolved "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= -querystring@0.2.0: +querystring@0.2.0, querystring@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= @@ -13942,6 +15544,11 @@ raf@^3.4.1: dependencies: performance-now "^2.1.0" +ramda@^0.21.0: + version "0.21.0" + resolved "https://registry.npmjs.org/ramda/-/ramda-0.21.0.tgz#a001abedb3ff61077d4ff1d577d44de77e8d0a35" + integrity sha1-oAGr7bP/YQd9T/HVd9RN536NCjU= + randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: version "2.1.0" resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -13972,6 +15579,14 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" +raw-loader@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/raw-loader/-/raw-loader-3.1.0.tgz#5e9d399a5a222cc0de18f42c3bc5e49677532b3f" + integrity sha512-lzUVMuJ06HF4rYveaz9Tv0WRlUMxJ0Y1hgSkkgg+50iEdaI0TthyEDe08KIHb0XsF6rn8WYTqPCaGTZg3sX+qA== + dependencies: + loader-utils "^1.1.0" + schema-utils "^2.0.1" + rc-progress@^2.5.2: version "2.5.2" resolved "https://registry.npmjs.org/rc-progress/-/rc-progress-2.5.2.tgz#ab01ba4e5d2fa36fc9f6f058b10b720e7315560c" @@ -14007,6 +15622,13 @@ react-app-polyfill@^1.0.6: regenerator-runtime "^0.13.3" whatwg-fetch "^3.0.0" +react-clientside-effect@^1.2.2: + version "1.2.2" + resolved "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.2.tgz#6212fb0e07b204e714581dd51992603d1accc837" + integrity sha512-nRmoyxeok5PBO6ytPvSjKp9xwXg9xagoTK1mMjwnQxqM9Hd7MNPl+LS1bOSOe+CV2+4fnEquc7H/S8QD3q697A== + dependencies: + "@babel/runtime" "^7.0.0" + react-dev-utils@^10.2.0: version "10.2.0" resolved "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-10.2.0.tgz#b11cc48aa2be2502fb3c27a50d1dfa95cfa9dfe0" @@ -14037,6 +15659,51 @@ react-dev-utils@^10.2.0: strip-ansi "6.0.0" text-table "0.2.0" +react-dev-utils@^9.0.0: + version "9.1.0" + resolved "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-9.1.0.tgz#3ad2bb8848a32319d760d0a84c56c14bdaae5e81" + integrity sha512-X2KYF/lIGyGwP/F/oXgGDF24nxDA2KC4b7AFto+eqzc/t838gpSGiaU8trTqHXOohuLxxc5qi1eDzsl9ucPDpg== + dependencies: + "@babel/code-frame" "7.5.5" + address "1.1.2" + browserslist "4.7.0" + chalk "2.4.2" + cross-spawn "6.0.5" + detect-port-alt "1.1.6" + escape-string-regexp "1.0.5" + filesize "3.6.1" + find-up "3.0.0" + fork-ts-checker-webpack-plugin "1.5.0" + global-modules "2.0.0" + globby "8.0.2" + gzip-size "5.1.1" + immer "1.10.0" + inquirer "6.5.0" + is-root "2.1.0" + loader-utils "1.2.3" + open "^6.3.0" + pkg-up "2.0.0" + react-error-overlay "^6.0.3" + recursive-readdir "2.2.2" + shell-quote "1.7.2" + sockjs-client "1.4.0" + strip-ansi "5.2.0" + text-table "0.2.0" + +react-docgen@^5.0.0: + version "5.3.0" + resolved "https://registry.npmjs.org/react-docgen/-/react-docgen-5.3.0.tgz#9aabde5e69f1993c8ba839fd9a86696504654589" + integrity sha512-hUrv69k6nxazOuOmdGeOpC/ldiKy7Qj/UFpxaQi0eDMrUFUTIPGtY5HJu7BggSmiyAMfREaESbtBL9UzdQ+hyg== + dependencies: + "@babel/core" "^7.7.5" + "@babel/runtime" "^7.7.6" + ast-types "^0.13.2" + commander "^2.19.0" + doctrine "^3.0.0" + neo-async "^2.6.1" + node-dir "^0.1.10" + strip-indent "^3.0.0" + react-dom@^16.12.0: version "16.13.0" resolved "https://registry.npmjs.org/react-dom/-/react-dom-16.13.0.tgz#cdde54b48eb9e8a0ca1b3dc9943d9bb409b81866" @@ -14047,16 +15714,62 @@ react-dom@^16.12.0: prop-types "^15.6.2" scheduler "^0.19.0" +react-dom@^16.8.3: + version "16.13.1" + resolved "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f" + integrity sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + scheduler "^0.19.1" + +react-draggable@^4.0.3: + version "4.2.0" + resolved "https://registry.npmjs.org/react-draggable/-/react-draggable-4.2.0.tgz#40cc5209082ca7d613104bf6daf31372cc0e1114" + integrity sha512-5wFq//gEoeTYprnd4ze8GrFc+Rbnx+9RkOMR3vk4EbWxj02U6L6T3yrlKeiw4X5CtjD2ma2+b3WujghcXNRzkw== + dependencies: + classnames "^2.2.5" + prop-types "^15.6.0" + +react-error-overlay@^6.0.3: + version "6.0.7" + resolved "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.7.tgz#1dcfb459ab671d53f660a991513cb2f0a0553108" + integrity sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA== + react-error-overlay@^6.0.6: version "6.0.6" resolved "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.6.tgz#ac4d9dc4c1b5c536c2c312bf66aa2b09bfa384e2" integrity sha512-Yzpno3enVzSrSCnnljmr4b/2KUQSMZaPuqmS26t9k4nW7uwJk6STWmH9heNjPuvqUTO3jOSPkHoKgO4+Dw7uIw== -react-fast-compare@^2.0.2: +react-fast-compare@^2.0.2, react-fast-compare@^2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9" integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw== +react-focus-lock@^2.1.0: + version "2.2.1" + resolved "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.2.1.tgz#1d12887416925dc53481914b7cedd39494a3b24a" + integrity sha512-47g0xYcCTZccdzKRGufepY8oZ3W1Qg+2hn6u9SHZ0zUB6uz/4K4xJe7yYFNZ1qT6m+2JDm82F6QgKeBTbjW4PQ== + dependencies: + "@babel/runtime" "^7.0.0" + focus-lock "^0.6.6" + prop-types "^15.6.2" + react-clientside-effect "^1.2.2" + use-callback-ref "^1.2.1" + use-sidecar "^1.0.1" + +react-helmet-async@^1.0.2: + version "1.0.4" + resolved "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.0.4.tgz#079ef10b7fefcaee6240fefd150711e62463cc97" + integrity sha512-KTGHE9sz8N7+fCkZ2a3vzXH9eIkiTNhL2NhKR7XzzQl3WsGlCHh76arauJUIiGdfhjeMp7DY7PkASAmYFXeJYg== + dependencies: + "@babel/runtime" "^7.3.4" + invariant "^2.2.4" + prop-types "^15.7.2" + react-fast-compare "^2.0.4" + shallowequal "^1.1.0" + react-helmet@5.2.1: version "5.2.1" resolved "https://registry.npmjs.org/react-helmet/-/react-helmet-5.2.1.tgz#16a7192fdd09951f8e0fe22ffccbf9bb3e591ffa" @@ -14067,16 +15780,53 @@ react-helmet@5.2.1: react-fast-compare "^2.0.2" react-side-effect "^1.1.0" +react-hotkeys@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/react-hotkeys/-/react-hotkeys-2.0.0.tgz#a7719c7340cbba888b0e9184f806a9ec0ac2c53f" + integrity sha512-3n3OU8vLX/pfcJrR3xJ1zlww6KS1kEJt0Whxc4FiGV+MJrQ1mYSYI3qS/11d2MJDFm8IhOXMTFQirfu6AVOF6Q== + dependencies: + prop-types "^15.6.1" + +react-inspector@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/react-inspector/-/react-inspector-4.0.1.tgz#0f888f78ff7daccbc7be5d452b20c96dc6d5fbb8" + integrity sha512-xSiM6CE79JBqSj8Fzd9dWBHv57tLTH7OM57GP3VrE5crzVF3D5Khce9w1Xcw75OAbvrA0Mi2vBneR1OajKmXFg== + dependencies: + "@babel/runtime" "^7.6.3" + is-dom "^1.0.9" + prop-types "^15.6.1" + react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.8.4: version "16.13.0" resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.0.tgz#0f37c3613c34fe6b37cd7f763a0d6293ab15c527" integrity sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA== -react-lifecycles-compat@^3.0.2: +react-lifecycles-compat@^3.0.2, react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== +react-popper-tooltip@^2.8.3: + version "2.10.1" + resolved "https://registry.npmjs.org/react-popper-tooltip/-/react-popper-tooltip-2.10.1.tgz#e10875f31916297c694d64a677d6f8fa0a48b4d1" + integrity sha512-cib8bKiyYcrIlHo9zXx81G0XvARfL8Jt+xum709MFCgQa3HTqTi4au3iJ9tm7vi7WU7ngnqbpWkMinBOtwo+IQ== + dependencies: + "@babel/runtime" "^7.7.4" + react-popper "^1.3.6" + +react-popper@^1.3.6: + version "1.3.7" + resolved "https://registry.npmjs.org/react-popper/-/react-popper-1.3.7.tgz#f6a3471362ef1f0d10a4963673789de1baca2324" + integrity sha512-nmqYTx7QVjCm3WUZLeuOomna138R1luC4EqkW3hxJUrAe+3eNz3oFCLYdnPwILfn0mX1Ew2c3wctrjlUMYYUww== + dependencies: + "@babel/runtime" "^7.1.2" + create-react-context "^0.3.0" + deep-equal "^1.1.1" + popper.js "^1.14.4" + prop-types "^15.6.1" + typed-styles "^0.0.7" + warning "^4.0.2" + react-router-dom@^5.1.2: version "5.1.2" resolved "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.1.2.tgz#06701b834352f44d37fbb6311f870f84c76b9c18" @@ -14173,6 +15923,35 @@ react-side-effect@^1.1.0: dependencies: shallowequal "^1.0.1" +react-sizeme@^2.6.7: + version "2.6.12" + resolved "https://registry.npmjs.org/react-sizeme/-/react-sizeme-2.6.12.tgz#ed207be5476f4a85bf364e92042520499455453e" + integrity sha512-tL4sCgfmvapYRZ1FO2VmBmjPVzzqgHA7kI8lSJ6JS6L78jXFNRdOZFpXyK6P1NBZvKPPCZxReNgzZNUajAerZw== + dependencies: + element-resize-detector "^1.2.1" + invariant "^2.2.4" + shallowequal "^1.1.0" + throttle-debounce "^2.1.0" + +react-syntax-highlighter@^11.0.2: + version "11.0.2" + resolved "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-11.0.2.tgz#4e3f376e752b20d2f54e4c55652fd663149e4029" + integrity sha512-kqmpM2OH5OodInbEADKARwccwSQWBfZi0970l5Jhp4h39q9Q65C4frNcnd6uHE5pR00W8pOWj9HDRntj2G4Rww== + dependencies: + "@babel/runtime" "^7.3.1" + highlight.js "~9.13.0" + lowlight "~1.11.0" + prismjs "^1.8.4" + refractor "^2.4.1" + +react-textarea-autosize@^7.1.0: + version "7.1.2" + resolved "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-7.1.2.tgz#70fdb333ef86bcca72717e25e623e90c336e2cda" + integrity sha512-uH3ORCsCa3C6LHxExExhF4jHoXYCQwE5oECmrRsunlspaDAbS4mGKNlWZqjLfInWtFQcf0o1n1jC/NGXFdUBCg== + dependencies: + "@babel/runtime" "^7.1.2" + prop-types "^15.6.0" + react-transition-group@^4.3.0: version "4.3.0" resolved "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.3.0.tgz#fea832e386cf8796c58b61874a3319704f5ce683" @@ -14211,6 +15990,15 @@ react@^16.12.0: object-assign "^4.1.1" prop-types "^15.6.2" +react@^16.8.3: + version "16.13.1" + resolved "https://registry.npmjs.org/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e" + integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + read-cmd-shim@^1.0.1, read-cmd-shim@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-1.0.5.tgz#87e43eba50098ba5a32d0ceb583ab8e43b961c16" @@ -14403,6 +16191,16 @@ realpath-native@^1.1.0: dependencies: util.promisify "^1.0.0" +recast@^0.14.7: + version "0.14.7" + resolved "https://registry.npmjs.org/recast/-/recast-0.14.7.tgz#4f1497c2b5826d42a66e8e3c9d80c512983ff61d" + integrity sha512-/nwm9pkrcWagN40JeJhkPaRxiHXBRkXyRh/hgU088Z/v+qCy+zIHHY6bC6o7NaKAxPqtE6nD8zBH1LfU0/Wx6A== + dependencies: + ast-types "0.11.3" + esprima "~4.0.0" + private "~0.1.5" + source-map "~0.6.1" + rechoir@^0.6.2: version "0.6.2" resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" @@ -14460,6 +16258,15 @@ redeyed@~2.1.0: dependencies: esprima "~4.0.0" +refractor@^2.4.1: + version "2.10.1" + resolved "https://registry.npmjs.org/refractor/-/refractor-2.10.1.tgz#166c32f114ed16fd96190ad21d5193d3afc7d34e" + integrity sha512-Xh9o7hQiQlDbxo5/XkOX6H+x/q8rmlmZKr97Ie1Q8ZM32IRRd3B/UxuA/yXDW79DBSXGWxm2yRTbcTVmAciJRw== + dependencies: + hastscript "^5.0.0" + parse-entities "^1.1.2" + prismjs "~1.17.0" + regenerate-unicode-properties@^8.1.0: version "8.1.0" resolved "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e" @@ -14487,6 +16294,11 @@ regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.3: resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5" integrity sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw== +regenerator-runtime@^0.13.4: + version "0.13.5" + resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" + integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA== + regenerator-transform@^0.14.0: version "0.14.1" resolved "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.1.tgz#3b2fce4e1ab7732c08f665dfdb314749c7ddd2fb" @@ -14767,7 +16579,7 @@ resolve@1.15.0: dependencies: path-parse "^1.0.6" -resolve@1.x, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.3.2, resolve@^1.8.1: +resolve@1.x, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.3.2, resolve@^1.8.1: version "1.15.1" resolved "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8" integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w== @@ -14845,7 +16657,7 @@ rimraf@2.6.3: dependencies: glob "^7.1.3" -rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@^2.7.1: +rimraf@^2.2.8, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@^2.7.1: version "2.7.1" resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -14905,6 +16717,11 @@ rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.5.3: dependencies: tslib "^1.9.0" +safe-buffer@5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" + integrity sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg== + safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -14978,6 +16795,14 @@ scheduler@^0.19.0: loose-envify "^1.1.0" object-assign "^4.1.1" +scheduler@^0.19.1: + version "0.19.1" + resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196" + integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + schema-utils@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" @@ -14987,6 +16812,14 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" +schema-utils@^2.0.1, schema-utils@^2.6.5: + version "2.6.5" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.5.tgz#c758f0a7e624263073d396e29cd40aa101152d8a" + integrity sha512-5KXuwKziQrTVHh8j/Uxz+QUbxkaLW9X/86NBlx/gnKgtsZA2GIVMUn17qWhRFwF8jdYb3Dig5hRO/W5mZqy6SQ== + dependencies: + ajv "^6.12.0" + ajv-keywords "^3.4.1" + schema-utils@^2.5.0, schema-utils@^2.6.0, schema-utils@^2.6.1, schema-utils@^2.6.4: version "2.6.4" resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.4.tgz#a27efbf6e4e78689d91872ee3ccfa57d7bdd0f53" @@ -15005,6 +16838,11 @@ select-hose@^2.0.0: resolved "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= +select@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d" + integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0= + selfsigned@^1.10.7: version "1.10.7" resolved "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz#da5819fd049d5574f28e88a9bcc6dbc6e6f3906b" @@ -15114,6 +16952,17 @@ serialize-javascript@^2.1.2: resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61" integrity sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ== +serve-favicon@^2.5.0: + version "2.5.0" + resolved "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.0.tgz#935d240cdfe0f5805307fdfe967d88942a2cbcf0" + integrity sha1-k10kDN/g9YBTB/3+ln2IlCosvPA= + dependencies: + etag "~1.8.1" + fresh "0.5.2" + ms "2.1.1" + parseurl "~1.3.2" + safe-buffer "5.1.1" + serve-index@^1.9.1: version "1.9.1" resolved "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" @@ -15204,7 +17053,12 @@ shallow-clone@^3.0.0: dependencies: kind-of "^6.0.2" -shallowequal@^1.0.1: +shallow-equal@^1.1.0: + version "1.2.1" + resolved "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz#4c16abfa56043aa20d050324efa68940b0da79da" + integrity sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA== + +shallowequal@^1.0.1, shallowequal@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ== @@ -15247,6 +17101,15 @@ shelljs@0.7.6: interpret "^1.0.0" rechoir "^0.6.2" +shelljs@^0.8.3: + version "0.8.3" + resolved "https://registry.npmjs.org/shelljs/-/shelljs-0.8.3.tgz#a7f3319520ebf09ee81275b2368adb286659b097" + integrity sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A== + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + shellwords@^0.1.1: version "0.1.1" resolved "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" @@ -15281,6 +17144,26 @@ simple-swizzle@^0.2.2: dependencies: is-arrayish "^0.3.1" +simplebar-react@^1.0.0-alpha.6: + version "1.2.3" + resolved "https://registry.npmjs.org/simplebar-react/-/simplebar-react-1.2.3.tgz#bd81fa9827628470e9470d06caef6ece15e1c882" + integrity sha512-1EOWJzFC7eqHUp1igD1/tb8GBv5aPQA5ZMvpeDnVkpNJ3jAuvmrL2kir3HuijlxhG7njvw9ssxjjBa89E5DrJg== + dependencies: + prop-types "^15.6.1" + simplebar "^4.2.3" + +simplebar@^4.2.3: + version "4.2.3" + resolved "https://registry.npmjs.org/simplebar/-/simplebar-4.2.3.tgz#dac40aced299c17928329eab3d5e6e795fafc10c" + integrity sha512-9no0pK7/1y+8/oTF3sy/+kx0PjQ3uk4cYwld5F1CJGk2gx+prRyUq8GRfvcVLq5niYWSozZdX73a2wIr1o9l/g== + dependencies: + can-use-dom "^0.1.0" + core-js "^3.0.1" + lodash.debounce "^4.0.8" + lodash.memoize "^4.1.2" + lodash.throttle "^4.1.1" + resize-observer-polyfill "^1.5.1" + sisteransi@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.4.tgz#386713f1ef688c7c0304dc4c0632898941cad2e3" @@ -15457,7 +17340,7 @@ source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, sourc resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@^0.5.0, source-map@^0.5.6: +source-map@^0.5.0, source-map@^0.5.6, source-map@^0.5.7: version "0.5.7" resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= @@ -15472,6 +17355,11 @@ sourcemap-codec@^1.4.1: resolved "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== +space-separated-tokens@^1.0.0: + version "1.1.5" + resolved "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899" + integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA== + spawn-error-forwarder@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz#1afd94738e999b0346d7b9fc373be55e07577029" @@ -15651,6 +17539,11 @@ stealthy-require@^1.1.1: resolved "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= +store2@^2.7.1: + version "2.10.0" + resolved "https://registry.npmjs.org/store2/-/store2-2.10.0.tgz#46b82bb91878daf1b0d56dec2f1d41e54d5103cf" + integrity sha512-tWEpK0snS2RPUq1i3R6OahfJNjWCQYNxq0+by1amCSuw0mXtymJpzmZIeYpA1UAa+7B0grCpNYIbDcd7AgTbFg== + stream-browserify@^2.0.1: version "2.0.2" resolved "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" @@ -15756,7 +17649,7 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string-width@^4.1.0, string-width@^4.2.0: +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: version "4.2.0" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== @@ -15765,7 +17658,7 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" -string.prototype.matchall@^4.0.2: +"string.prototype.matchall@^4.0.0 || ^3.0.1", string.prototype.matchall@^4.0.2: version "4.0.2" resolved "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.2.tgz#48bb510326fb9fdeb6a33ceaa81a6ea04ef7648e" integrity sha512-N/jp6O5fMf9os0JU3E72Qhf590RSRZU/ungsL/qJUYVTNv7hTG0P/dbPjxINVN9jpscu3nzYwKESU3P3RY5tOg== @@ -15777,6 +17670,22 @@ string.prototype.matchall@^4.0.2: regexp.prototype.flags "^1.3.0" side-channel "^1.0.2" +string.prototype.padend@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.0.tgz#dc08f57a8010dc5c153550318f67e13adbb72ac3" + integrity sha512-3aIv8Ffdp8EZj8iLwREGpQaUZiPyrWrpzMBHvkiSW/bK/EGve9np07Vwy7IJ5waydpGXzQZu/F8Oze2/IWkBaA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + +string.prototype.padstart@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/string.prototype.padstart/-/string.prototype.padstart-3.1.0.tgz#b47c087540d0710be5a49375751a0a627bd4ff90" + integrity sha512-envqZvUp2JItI+OeQ5UAh1ihbAV5G/2bixTojvlIa090GGqF+NQRxbWb2nv9fTGrZABv6+pE6jXoAZhhS2k4Hw== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + string.prototype.trimleft@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74" @@ -15826,6 +17735,13 @@ stringify-package@^1.0.0, stringify-package@^1.0.1: resolved "https://registry.npmjs.org/stringify-package/-/stringify-package-1.0.1.tgz#e5aa3643e7f74d0f28628b72f3dad5cecfc3ba85" integrity sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg== +strip-ansi@5.2.0, strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + strip-ansi@6.0.0, strip-ansi@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" @@ -15847,13 +17763,6 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" -strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: - version "5.2.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" - integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== - dependencies: - ansi-regex "^4.1.0" - strip-bom@4.0.0, strip-bom@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" @@ -15935,6 +17844,14 @@ style-loader@0.23.1: loader-utils "^1.1.0" schema-utils "^1.0.0" +style-loader@^1.0.0: + version "1.1.3" + resolved "https://registry.npmjs.org/style-loader/-/style-loader-1.1.3.tgz#9e826e69c683c4d9bf9db924f85e9abb30d5e200" + integrity sha512-rlkH7X/22yuwFYK357fMN/BxYOorfnfq0eD7+vqlemSK4wEcejFF1dg4zxP0euBW8NrYx2WZzZ8PPFevr7D+Kw== + dependencies: + loader-utils "^1.2.3" + schema-utils "^2.6.4" + stylehacks@^4.0.0: version "4.0.3" resolved "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5" @@ -16017,6 +17934,14 @@ symbol-tree@^3.2.2: resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== +symbol.prototype.description@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/symbol.prototype.description/-/symbol.prototype.description-1.0.2.tgz#f325e1e6ad534b3b29c9c3ca73c136c9ce03c5e2" + integrity sha512-2CW5SU4/Ki1cYOOHcL2cXK4rxSg5hCU1TwZ7X4euKhV9VnfqKslh7T6/UyKkubA8cq2tOmsOv7m3ZUmQslBRuw== + dependencies: + es-abstract "^1.17.0-next.1" + has-symbols "^1.0.1" + table@^5.2.3: version "5.4.6" resolved "https://registry.npmjs.org/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" @@ -16045,6 +17970,20 @@ tar@^4.4.10, tar@^4.4.12, tar@^4.4.13, tar@^4.4.8: safe-buffer "^5.1.2" yallist "^3.0.3" +telejson@^3.2.0: + version "3.3.0" + resolved "https://registry.npmjs.org/telejson/-/telejson-3.3.0.tgz#6d814f3c0d254d5c4770085aad063e266b56ad03" + integrity sha512-er08AylQ+LEbDLp1GRezORZu5wKOHaBczF6oYJtgC3Idv10qZ8A3p6ffT+J5BzDKkV9MqBvu8HAKiIIOp6KJ2w== + dependencies: + "@types/is-function" "^1.0.0" + global "^4.4.0" + is-function "^1.0.1" + is-regex "^1.0.4" + is-symbol "^1.0.3" + isobject "^4.0.0" + lodash "^4.17.15" + memoizerific "^1.11.3" + temp-dir@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" @@ -16083,6 +18022,11 @@ term-size@^1.2.0: dependencies: execa "^0.7.0" +term-size@^2.1.0: + version "2.2.0" + resolved "https://registry.npmjs.org/term-size/-/term-size-2.2.0.tgz#1f16adedfe9bdc18800e1776821734086fcc6753" + integrity sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw== + terminal-link@^2.0.0: version "2.1.1" resolved "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" @@ -16121,6 +18065,21 @@ terser-webpack-plugin@^1.4.3: webpack-sources "^1.4.0" worker-farm "^1.7.0" +terser-webpack-plugin@^2.1.2: + version "2.3.5" + resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.5.tgz#5ad971acce5c517440ba873ea4f09687de2f4a81" + integrity sha512-WlWksUoq+E4+JlJ+h+U+QUzXpcsMSSNXkDy9lBVkSqDn1w23Gg29L/ary9GeJVYCGiNJJX7LnVc4bwL1N3/g1w== + dependencies: + cacache "^13.0.1" + find-cache-dir "^3.2.0" + jest-worker "^25.1.0" + p-limit "^2.2.2" + schema-utils "^2.6.4" + serialize-javascript "^2.1.2" + source-map "^0.6.1" + terser "^4.4.3" + webpack-sources "^1.4.3" + terser@^4.1.2: version "4.6.4" resolved "https://registry.npmjs.org/terser/-/terser-4.6.4.tgz#40a0b37afbe5b57e494536815efa68326840fc00" @@ -16239,6 +18198,11 @@ timsort@^0.3.0: resolved "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= +tiny-emitter@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" + integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== + tiny-invariant@^1.0.2: version "1.1.0" resolved "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875" @@ -16374,6 +18338,11 @@ trim-off-newlines@^1.0.0: resolved "https://registry.npmjs.org/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3" integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM= +ts-dedent@^1.1.0: + version "1.1.1" + resolved "https://registry.npmjs.org/ts-dedent/-/ts-dedent-1.1.1.tgz#68fad040d7dbd53a90f545b450702340e17d18f3" + integrity sha512-UGTRZu1evMw4uTPyYF66/KFd22XiU+jMaIuHrkIHQ2GivAXVlLV0v/vHrpOuTRf9BmpNHi/SO7Vd0rLu0y57jg== + ts-easing@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/ts-easing/-/ts-easing-0.2.0.tgz#c8a8a35025105566588d87dbda05dd7fbfa5a4ec" @@ -16427,7 +18396,7 @@ ts-pnp@^1.1.2: resolved "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.1.6.tgz#389a24396d425a0d3162e96d2b4638900fdc289a" integrity sha512-CrG5GqAAzMT7144Cl+UIFP7mz/iIhiy+xQ6GGcnjTezhALT02uPMRw7tgDSESgB5MsfKt55+GPWw4ir1kVtMIQ== -tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0: +tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.11.1" resolved "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA== @@ -16506,6 +18475,11 @@ type@^2.0.0: resolved "https://registry.npmjs.org/type/-/type-2.0.0.tgz#5f16ff6ef2eb44f260494dae271033b29c09a9c3" integrity sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow== +typed-styles@^0.0.7: + version "0.0.7" + resolved "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.7.tgz#93392a008794c4595119ff62dde6809dbc40a3d9" + integrity sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q== + typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" @@ -16561,6 +18535,11 @@ undefsafe@^2.0.2: dependencies: debug "^2.2.0" +unfetch@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/unfetch/-/unfetch-4.1.0.tgz#6ec2dd0de887e58a4dee83a050ded80ffc4137db" + integrity sha512-crP/n3eAPUJxZXM9T80/yv0YhkTEx2K1D3h7D1AJM6fzsWZrxdyRuLN0JH/dkZh1LNH8LxCnBzoPFCPbb2iGpg== + unicode-canonical-property-names-ecmascript@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" @@ -16656,7 +18635,7 @@ unpipe@1.0.0, unpipe@~1.0.0: resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= -unquote@~1.1.1: +unquote@^1.1.0, unquote@~1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ= @@ -16717,7 +18696,7 @@ url-join@^4.0.0: resolved "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7" integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA== -url-loader@2.3.0: +url-loader@2.3.0, url-loader@^2.0.1: version "2.3.0" resolved "https://registry.npmjs.org/url-loader/-/url-loader-2.3.0.tgz#e0e2ef658f003efb8ca41b0f3ffbf76bab88658b" integrity sha512-goSdg8VY+7nPZKUEChZSEtW5gjbS66USIGCeSJ1OVOJ7Yfuh/36YxCwMi5HVEJh6mqUYOoy3NJ0vlOMrWsSHog== @@ -16749,12 +18728,25 @@ url@^0.11.0: punycode "1.3.2" querystring "0.2.0" +use-callback-ref@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.2.1.tgz#898759ccb9e14be6c7a860abafa3ffbd826c89bb" + integrity sha512-C3nvxh0ZpaOxs9RCnWwAJ+7bJPwQI8LHF71LzbQ3BvzH5XkdtlkMadqElGevg5bYBDFip4sAnD4m06zAKebg1w== + +use-sidecar@^1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.0.2.tgz#e72f582a75842f7de4ef8becd6235a4720ad8af6" + integrity sha512-287RZny6m5KNMTb/Kq9gmjafi7lQL0YHO1lYolU6+tY1h9+Z3uCtkJJ3OSOq3INwYf2hBryCcDh4520AhJibMA== + dependencies: + detect-node "^2.0.4" + tslib "^1.9.3" + use@^3.1.0: version "3.1.1" resolved "https://registry.npmjs.org/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== -util-deprecate@^1.0.1, util-deprecate@~1.0.1: +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= @@ -16904,6 +18896,13 @@ walker@^1.0.7, walker@~1.0.5: dependencies: makeerror "1.0.x" +warning@^4.0.2, warning@^4.0.3: + version "4.0.3" + resolved "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" + integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== + dependencies: + loose-envify "^1.0.0" + watchpack@^1.6.0: version "1.6.0" resolved "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" @@ -16932,7 +18931,7 @@ webidl-conversions@^4.0.2: resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== -webpack-dev-middleware@^3.7.2: +webpack-dev-middleware@^3.7.0, webpack-dev-middleware@^3.7.2: version "3.7.2" resolved "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz#0019c3db716e3fa5cecbf64f2ab88a74bab331f3" integrity sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw== @@ -17021,6 +19020,16 @@ webpack-dev-server@^3.10.3: ws "^6.2.1" yargs "12.0.5" +webpack-hot-middleware@^2.25.0: + version "2.25.0" + resolved "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.25.0.tgz#4528a0a63ec37f8f8ef565cf9e534d57d09fe706" + integrity sha512-xs5dPOrGPCzuRXNi8F6rwhawWvQQkeli5Ro48PRuQh8pYPCPmNnltP9itiUPT4xI8oW+y0m59lyyeQk54s5VgA== + dependencies: + ansi-html "0.0.7" + html-entities "^1.2.0" + querystring "^0.2.0" + strip-ansi "^3.0.0" + webpack-log@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f" @@ -17047,6 +19056,13 @@ webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack- source-list-map "^2.0.0" source-map "~0.6.1" +webpack-virtual-modules@^0.2.0: + version "0.2.1" + resolved "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.2.1.tgz#8ab73d4df0fd37ed27bb8d823bc60ea7266c8bf7" + integrity sha512-0PWBlxyt4uGDofooIEanWhhyBOHdd+lr7QpYNDLC7/yc5lqJT8zlc04MTIBnKj+c2BlQNNuwE5er/Tg4wowHzA== + dependencies: + debug "^3.0.0" + webpack@4.41.5: version "4.41.5" resolved "https://registry.npmjs.org/webpack/-/webpack-4.41.5.tgz#3210f1886bce5310e62bb97204d18c263341b77c" @@ -17076,6 +19092,35 @@ webpack@4.41.5: watchpack "^1.6.0" webpack-sources "^1.4.1" +webpack@^4.33.0, webpack@^4.38.0: + version "4.42.0" + resolved "https://registry.npmjs.org/webpack/-/webpack-4.42.0.tgz#b901635dd6179391d90740a63c93f76f39883eb8" + integrity sha512-EzJRHvwQyBiYrYqhyjW9AqM90dE4+s1/XtCfn7uWg6cS72zH+2VPFAlsnW0+W0cDi0XRjNKUMoJtpSi50+Ph6w== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-module-context" "1.8.5" + "@webassemblyjs/wasm-edit" "1.8.5" + "@webassemblyjs/wasm-parser" "1.8.5" + acorn "^6.2.1" + ajv "^6.10.2" + ajv-keywords "^3.4.1" + chrome-trace-event "^1.0.2" + enhanced-resolve "^4.1.0" + eslint-scope "^4.0.3" + json-parse-better-errors "^1.0.2" + loader-runner "^2.4.0" + loader-utils "^1.2.3" + memory-fs "^0.4.1" + micromatch "^3.1.10" + mkdirp "^0.5.1" + neo-async "^2.6.1" + node-libs-browser "^2.2.1" + schema-utils "^1.0.0" + tapable "^1.1.3" + terser-webpack-plugin "^1.4.3" + watchpack "^1.6.0" + webpack-sources "^1.4.1" + webpack@^4.41.6: version "4.41.6" resolved "https://registry.npmjs.org/webpack/-/webpack-4.41.6.tgz#12f2f804bf6542ef166755050d4afbc8f66ba7e1" @@ -17187,6 +19232,13 @@ widest-line@^2.0.0: dependencies: string-width "^2.1.1" +widest-line@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" + integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== + dependencies: + string-width "^4.0.0" + windows-release@^3.1.0: version "3.2.0" resolved "https://registry.npmjs.org/windows-release/-/windows-release-3.2.0.tgz#8122dad5afc303d833422380680a79cdfa91785f" From 044fad2fbfc8da071b55bd26d5a9bc0a6b290729 Mon Sep 17 00:00:00 2001 From: Muhammad Rivki Date: Sat, 21 Mar 2020 21:59:25 +0700 Subject: [PATCH 003/146] chore: add run storybook script at root folder --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 7799dc771b..66eea1d006 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "build": "lerna run build", "test": "cross-env CI=true lerna run test -- --coverage", "create-plugin": "backstage-cli create-plugin", - "lint": "lerna run lint" + "lint": "lerna run lint", + "storybook": "yarn workspace @spotify-backstage/core storybook" }, "workspaces": { "packages": [ From abe9aab7f95b779dacfeba6ee96ef69c7afca177 Mon Sep 17 00:00:00 2001 From: Adam Laiacano Date: Sat, 21 Mar 2020 20:43:55 -0400 Subject: [PATCH 004/146] add heroku deployment steps --- Dockerfile | 7 +++++++ default.conf.template | 8 ++++++++ 2 files changed, 15 insertions(+) create mode 100644 default.conf.template diff --git a/Dockerfile b/Dockerfile index 39d51a35f2..3220e46f5f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,3 +15,10 @@ RUN yarn build FROM nginx:mainline COPY --from=builder /app/packages/app/build /usr/share/nginx/html + +# Run nginx as root +RUN sed -i 's/user nginx.*$//' /etc/nginx/nginx.conf + +# Copy in the nginx conf template and replace "$PORT" with the environment variable set by heroku +COPY default.conf.template /etc/nginx/conf.d/default.conf.template +CMD /bin/bash -c "envsubst '\$PORT' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf" && nginx -g 'daemon off;' diff --git a/default.conf.template b/default.conf.template new file mode 100644 index 0000000000..9d9ff7f3e2 --- /dev/null +++ b/default.conf.template @@ -0,0 +1,8 @@ +server { + listen $PORT default_server; + + location / { + root /usr/share/nginx/html; + index index.html; + } +} \ No newline at end of file From 68134404a639e59b6cb9c2776bebbc2aa98b8e06 Mon Sep 17 00:00:00 2001 From: Muhammad Rivki Date: Sun, 22 Mar 2020 14:09:54 +0700 Subject: [PATCH 005/146] fix: mismatch package version --- yarn.lock | 1845 +++++++++++++++++++++-------------------------------- 1 file changed, 741 insertions(+), 1104 deletions(-) diff --git a/yarn.lock b/yarn.lock index e5de24c4ec..9300d907cb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16,58 +16,16 @@ dependencies: "@babel/highlight" "^7.8.3" -"@babel/compat-data@^7.8.4", "@babel/compat-data@^7.8.6": - version "7.8.6" - resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.8.6.tgz#7eeaa0dfa17e50c7d9c0832515eee09b56f04e35" - integrity sha512-CurCIKPTkS25Mb8mz267vU95vy+TyUpnctEX2lV33xWNmHAfjruztgiPBbXZRh3xZZy1CYvGx6XfxyTVS+sk7Q== +"@babel/compat-data@^7.8.6", "@babel/compat-data@^7.9.0": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.9.0.tgz#04815556fc90b0c174abd2c0c1bb966faa036a6c" + integrity sha512-zeFQrr+284Ekvd9e7KAX954LkapWiOmQtsfHirhxqfdlX6MEC32iRE+pqUGlYIBchdevaCwvzxWGSy/YBNI85g== dependencies: - browserslist "^4.8.5" + browserslist "^4.9.1" invariant "^2.2.4" semver "^5.5.0" -"@babel/core@7.8.4": - version "7.8.4" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.8.4.tgz#d496799e5c12195b3602d0fddd77294e3e38e80e" - integrity sha512-0LiLrB2PwrVI+a2/IEskBopDYSd8BCb3rOvH7D5tzoWd696TBEduBvuLVm4Nx6rltrLZqvI3MCalB2K2aVzQjA== - dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.8.4" - "@babel/helpers" "^7.8.4" - "@babel/parser" "^7.8.4" - "@babel/template" "^7.8.3" - "@babel/traverse" "^7.8.4" - "@babel/types" "^7.8.3" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.1" - json5 "^2.1.0" - lodash "^4.17.13" - resolve "^1.3.2" - semver "^5.4.1" - source-map "^0.5.0" - -"@babel/core@^7.1.0", "@babel/core@^7.4.5", "@babel/core@^7.7.5": - version "7.8.6" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.8.6.tgz#27d7df9258a45c2e686b6f18b6c659e563aa4636" - integrity sha512-Sheg7yEJD51YHAvLEV/7Uvw95AeWqYPL3Vk3zGujJKIhJ+8oLw2ALaf3hbucILhKsgSoADOvtKRJuNVdcJkOrg== - dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.8.6" - "@babel/helpers" "^7.8.4" - "@babel/parser" "^7.8.6" - "@babel/template" "^7.8.6" - "@babel/traverse" "^7.8.6" - "@babel/types" "^7.8.6" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.1" - json5 "^2.1.0" - lodash "^4.17.13" - resolve "^1.3.2" - semver "^5.4.1" - source-map "^0.5.0" - -"@babel/core@^7.9.0": +"@babel/core@7.9.0", "@babel/core@^7.1.0", "@babel/core@^7.4.5", "@babel/core@^7.7.5", "@babel/core@^7.9.0": version "7.9.0" resolved "https://registry.npmjs.org/@babel/core/-/core-7.9.0.tgz#ac977b538b77e132ff706f3b8a4dbad09c03c56e" integrity sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w== @@ -89,17 +47,7 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/generator@^7.4.0", "@babel/generator@^7.8.4", "@babel/generator@^7.8.6": - version "7.8.6" - resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.8.6.tgz#57adf96d370c9a63c241cd719f9111468578537a" - integrity sha512-4bpOR5ZBz+wWcMeVtcf7FbjcFzCp+817z2/gHNncIRcM9MmKzUhtWCYAq27RAfUrAFwb+OCG1s9WEaVxfi6cjg== - dependencies: - "@babel/types" "^7.8.6" - jsesc "^2.5.1" - lodash "^4.17.13" - source-map "^0.5.0" - -"@babel/generator@^7.9.0": +"@babel/generator@^7.4.0", "@babel/generator@^7.9.0": version "7.9.0" resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.9.0.tgz#0f67adea4ec39dad6e63345f70eec33014d78c89" integrity sha512-onl4Oy46oGCzymOXtKMQpI7VXtCbTSHK1kqBydZ6AmzuNcacEVqGk9tZtAS+48IA9IstZcDCgIg8hQKnb7suRw== @@ -124,30 +72,39 @@ "@babel/helper-explode-assignable-expression" "^7.8.3" "@babel/types" "^7.8.3" -"@babel/helper-builder-react-jsx@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.8.3.tgz#dee98d7d79cc1f003d80b76fe01c7f8945665ff6" - integrity sha512-JT8mfnpTkKNCboTqZsQTdGo3l3Ik3l7QIt9hh0O9DYiwVel37VoJpILKM4YFbP2euF32nkQSb+F9cUk9b7DDXQ== +"@babel/helper-builder-react-jsx-experimental@^7.9.0": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.9.0.tgz#066d80262ade488f9c1b1823ce5db88a4cedaa43" + integrity sha512-3xJEiyuYU4Q/Ar9BsHisgdxZsRlsShMe90URZ0e6przL26CCs8NJbDoxH94kKT17PcxlMhsCAwZd90evCo26VQ== dependencies: - "@babel/types" "^7.8.3" - esutils "^2.0.0" + "@babel/helper-annotate-as-pure" "^7.8.3" + "@babel/helper-module-imports" "^7.8.3" + "@babel/types" "^7.9.0" -"@babel/helper-call-delegate@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.8.3.tgz#de82619898aa605d409c42be6ffb8d7204579692" - integrity sha512-6Q05px0Eb+N4/GTyKPPvnkig7Lylw+QzihMpws9iiZQv7ZImf84ZsZpQH7QoWN4n4tm81SnSzPgHw2qtO0Zf3A== +"@babel/helper-builder-react-jsx@^7.9.0": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.9.0.tgz#16bf391990b57732700a3278d4d9a81231ea8d32" + integrity sha512-weiIo4gaoGgnhff54GQ3P5wsUQmnSwpkvU0r6ZHq6TzoSzKy4JxHEgnxNytaKbov2a9z/CVNyzliuCOUPEX3Jw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.8.3" + "@babel/types" "^7.9.0" + +"@babel/helper-call-delegate@^7.8.7": + version "7.8.7" + resolved "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.8.7.tgz#28a279c2e6c622a6233da548127f980751324cab" + integrity sha512-doAA5LAKhsFCR0LAFIf+r2RSMmC+m8f/oQ+URnUET/rWeEzC0yTRmAGyWkD4sSu3xwbS7MYQ2u+xlt1V5R56KQ== dependencies: "@babel/helper-hoist-variables" "^7.8.3" "@babel/traverse" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/types" "^7.8.7" -"@babel/helper-compilation-targets@^7.8.4", "@babel/helper-compilation-targets@^7.8.6": - version "7.8.6" - resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.8.6.tgz#015b85db69e3a34240d5c2b761fc53eb9695f09c" - integrity sha512-UrJdk27hKVJSnibFcUWYLkCL0ZywTUoot8yii1lsHJcvwrypagmYKjHLMWivQPm4s6GdyygCL8fiH5EYLxhQwQ== +"@babel/helper-compilation-targets@^7.8.7": + version "7.8.7" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.8.7.tgz#dac1eea159c0e4bd46e309b5a1b04a66b53c1dde" + integrity sha512-4mWm8DCK2LugIS+p1yArqvG1Pf162upsIsjE7cNBjez+NjliQpVhj20obE520nao0o14DaTnFJv+Fw5a0JpoUw== dependencies: "@babel/compat-data" "^7.8.6" - browserslist "^4.8.5" + browserslist "^4.9.1" invariant "^2.2.4" levenary "^1.1.1" semver "^5.5.0" @@ -164,14 +121,14 @@ "@babel/helper-replace-supers" "^7.8.6" "@babel/helper-split-export-declaration" "^7.8.3" -"@babel/helper-create-regexp-features-plugin@^7.8.3": - version "7.8.6" - resolved "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.6.tgz#7fa040c97fb8aebe1247a5c645330c32d083066b" - integrity sha512-bPyujWfsHhV/ztUkwGHz/RPV1T1TDEsSZDsN42JPehndA+p1KKTh3npvTadux0ZhCrytx9tvjpWNowKby3tM6A== +"@babel/helper-create-regexp-features-plugin@^7.8.3", "@babel/helper-create-regexp-features-plugin@^7.8.8": + version "7.8.8" + resolved "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.8.tgz#5d84180b588f560b7864efaeea89243e58312087" + integrity sha512-LYVPdwkrQEiX9+1R29Ld/wTrmQu1SSKYnuOk3g0CkcZMA1p0gsNxJFj/3gBdaJ7Cg0Fnek5z0DsMULePP7Lrqg== dependencies: "@babel/helper-annotate-as-pure" "^7.8.3" "@babel/helper-regex" "^7.8.3" - regexpu-core "^4.6.0" + regexpu-core "^4.7.0" "@babel/helper-define-map@^7.8.3": version "7.8.3" @@ -227,19 +184,6 @@ dependencies: "@babel/types" "^7.8.3" -"@babel/helper-module-transforms@^7.8.3": - version "7.8.6" - resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.8.6.tgz#6a13b5eecadc35692047073a64e42977b97654a4" - integrity sha512-RDnGJSR5EFBJjG3deY0NiL0K9TO8SXxS9n/MPsbPK/s9LbQymuLNtlzvDiNS7IpecuL45cMeLVkA+HfmlrnkRg== - dependencies: - "@babel/helper-module-imports" "^7.8.3" - "@babel/helper-replace-supers" "^7.8.6" - "@babel/helper-simple-access" "^7.8.3" - "@babel/helper-split-export-declaration" "^7.8.3" - "@babel/template" "^7.8.6" - "@babel/types" "^7.8.6" - lodash "^4.17.13" - "@babel/helper-module-transforms@^7.9.0": version "7.9.0" resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz#43b34dfe15961918707d247327431388e9fe96e5" @@ -323,25 +267,16 @@ "@babel/traverse" "^7.8.3" "@babel/types" "^7.8.3" -"@babel/helpers@^7.8.4": - version "7.8.4" - resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.8.4.tgz#754eb3ee727c165e0a240d6c207de7c455f36f73" - integrity sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w== - dependencies: - "@babel/template" "^7.8.3" - "@babel/traverse" "^7.8.4" - "@babel/types" "^7.8.3" - "@babel/helpers@^7.9.0": - version "7.9.0" - resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.0.tgz#ab2c1bc4821af766cab51d4868a5038874ea5a12" - integrity sha512-/9GvfYTCG1NWCNwDj9e+XlnSCmWW/r9T794Xi58vPF9WCcnZCAZ0kWLSn54oqP40SUvh1T2G6VwKmFO5AOlW3A== + version "7.9.2" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.2.tgz#b42a81a811f1e7313b88cba8adc66b3d9ae6c09f" + integrity sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA== dependencies: "@babel/template" "^7.8.3" "@babel/traverse" "^7.9.0" "@babel/types" "^7.9.0" -"@babel/highlight@^7.0.0": +"@babel/highlight@^7.0.0", "@babel/highlight@^7.8.3": version "7.9.0" resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz#4e9b45ccb82b79607271b2979ad82c7b68163079" integrity sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ== @@ -350,24 +285,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/highlight@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797" - integrity sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg== - dependencies: - chalk "^2.0.0" - esutils "^2.0.2" - js-tokens "^4.0.0" - -"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.4.3", "@babel/parser@^7.7.5", "@babel/parser@^7.8.4", "@babel/parser@^7.8.6": - version "7.8.6" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.8.6.tgz#ba5c9910cddb77685a008e3c587af8d27b67962c" - integrity sha512-trGNYSfwq5s0SgM1BMEB8hX3NDmO7EP2wsDGDexiaKMB92BaRpS+qZfpkMqUBhcsOTBwNy9B/jieo4ad/t/z2g== - -"@babel/parser@^7.9.0": - version "7.9.0" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.9.0.tgz#f821b32313f07ee570976d3f6238e8d2d66e0a8e" - integrity sha512-Iwyp00CZsypoNJcpXCbq3G4tcDgphtlMwMVrMhhZ//XBkqjXF7LW6V511yk0+pBX3ZwwGnPea+pTKNJiqA7pUg== +"@babel/parser@^7.1.0", "@babel/parser@^7.4.3", "@babel/parser@^7.7.0", "@babel/parser@^7.7.5", "@babel/parser@^7.8.6", "@babel/parser@^7.9.0": + version "7.9.2" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.9.2.tgz#4e767f424b479c514077544484d1f9bdba7f1158" + integrity sha512-2jyvKdoOS1aWAFL2rjJZmamyDDkPCx/AAz4/Wh1Dfxvw8qqnOvek/ZlHQ2noO/o8JpnXa/WiUUFOv48meBKkpA== "@babel/plugin-proposal-async-generator-functions@^7.8.3": version "7.8.3" @@ -411,7 +332,7 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-json-strings" "^7.8.0" -"@babel/plugin-proposal-nullish-coalescing-operator@^7.8.3": +"@babel/plugin-proposal-nullish-coalescing-operator@7.8.3", "@babel/plugin-proposal-nullish-coalescing-operator@^7.8.3": version "7.8.3" resolved "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz#e4572253fdeed65cddeecfdab3f928afeb2fd5d2" integrity sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw== @@ -419,7 +340,7 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" -"@babel/plugin-proposal-numeric-separator@7.8.3": +"@babel/plugin-proposal-numeric-separator@7.8.3", "@babel/plugin-proposal-numeric-separator@^7.8.3": version "7.8.3" resolved "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.8.3.tgz#5d6769409699ec9b3b68684cd8116cedff93bad8" integrity sha512-jWioO1s6R/R+wEHizfaScNsAx+xKgwTLNXSh7tTC4Usj3ItsPEhYkEpU4h+lpnBwq7NBVOJXfO6cRFYcX69JUQ== @@ -427,7 +348,7 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-numeric-separator" "^7.8.3" -"@babel/plugin-proposal-object-rest-spread@^7.6.2": +"@babel/plugin-proposal-object-rest-spread@^7.6.2", "@babel/plugin-proposal-object-rest-spread@^7.9.0": version "7.9.0" resolved "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.0.tgz#a28993699fc13df165995362693962ba6b061d6f" integrity sha512-UgqBv6bjq4fDb8uku9f+wcm1J7YxJ5nT7WO/jBr0cl0PLKb7t1O6RNR1kZbjgx2LQtsDI9hwoQVmn0yhXeQyow== @@ -435,14 +356,6 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-object-rest-spread" "^7.8.0" -"@babel/plugin-proposal-object-rest-spread@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.8.3.tgz#eb5ae366118ddca67bed583b53d7554cad9951bb" - integrity sha512-8qvuPwU/xxUCt78HocNlv0mXXo0wdh9VT1R04WU8HGOfaOob26pF+9P5/lYjN/q7DHOX1bvX60hnhOvuQUJdbA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-object-rest-spread" "^7.8.0" - "@babel/plugin-proposal-optional-catch-binding@^7.8.3": version "7.8.3" resolved "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz#9dee96ab1650eed88646ae9734ca167ac4a9c5c9" @@ -451,20 +364,20 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" -"@babel/plugin-proposal-optional-chaining@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.8.3.tgz#ae10b3214cb25f7adb1f3bc87ba42ca10b7e2543" - integrity sha512-QIoIR9abkVn+seDE3OjA08jWcs3eZ9+wJCKSRgo3WdEU2csFYgdScb+8qHB3+WXsGJD55u+5hWCISI7ejXS+kg== +"@babel/plugin-proposal-optional-chaining@7.9.0", "@babel/plugin-proposal-optional-chaining@^7.9.0": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz#31db16b154c39d6b8a645292472b98394c292a58" + integrity sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w== dependencies: "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-optional-chaining" "^7.8.0" -"@babel/plugin-proposal-unicode-property-regex@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.3.tgz#b646c3adea5f98800c9ab45105ac34d06cd4a47f" - integrity sha512-1/1/rEZv2XGweRwwSkLpY+s60za9OZ1hJs4YDqFHCw0kYWYwL5IFljVY1MYBL+weT1l9pokDO2uhSTLVxzoHkQ== +"@babel/plugin-proposal-unicode-property-regex@^7.4.4", "@babel/plugin-proposal-unicode-property-regex@^7.8.3": + version "7.8.8" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.8.tgz#ee3a95e90cdc04fe8cd92ec3279fa017d68a0d1d" + integrity sha512-EVhjVsMpbhLw9ZfHWSx2iy13Q8Z/eg8e8ccVWt23sWQK5l1UdkoLJPN5w69UA4uITGBnEZD2JOe4QOHycYKv8A== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.8.3" + "@babel/helper-create-regexp-features-plugin" "^7.8.8" "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-async-generators@^7.8.0": @@ -523,7 +436,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-numeric-separator@^7.8.3": +"@babel/plugin-syntax-numeric-separator@^7.8.0", "@babel/plugin-syntax-numeric-separator@^7.8.3": version "7.8.3" resolved "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz#0e3fb63e09bea1b11e96467271c8308007e7c41f" integrity sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw== @@ -596,10 +509,10 @@ "@babel/helper-plugin-utils" "^7.8.3" lodash "^4.17.13" -"@babel/plugin-transform-classes@^7.8.3", "@babel/plugin-transform-classes@^7.8.6": - version "7.8.6" - resolved "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.8.6.tgz#77534447a477cbe5995ae4aee3e39fbc8090c46d" - integrity sha512-k9r8qRay/R6v5aWZkrEclEhKO6mc1CCQr2dLsVHBmOQiMpN6I2bpjX3vgnldUWeEI1GHVNByULVxZ4BdP4Hmdg== +"@babel/plugin-transform-classes@^7.9.0": + version "7.9.2" + resolved "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.2.tgz#8603fc3cc449e31fdbdbc257f67717536a11af8d" + integrity sha512-TC2p3bPzsfvSsqBZo0kJnuelnoK9O3welkUpqSqBQuBF6R5MN2rysopri8kNvtlGIb2jmUO7i15IooAZJjZuMQ== dependencies: "@babel/helper-annotate-as-pure" "^7.8.3" "@babel/helper-define-map" "^7.8.3" @@ -618,13 +531,13 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-transform-destructuring@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.8.3.tgz#20ddfbd9e4676906b1056ee60af88590cc7aaa0b" - integrity sha512-H4X646nCkiEcHZUZaRkhE2XVsoz0J/1x3VVujnn96pSoGCtKPA99ZZA+va+gK+92Zycd6OBKCD8tDb/731bhgQ== + version "7.8.8" + resolved "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.8.8.tgz#fadb2bc8e90ccaf5658de6f8d4d22ff6272a2f4b" + integrity sha512-eRJu4Vs2rmttFCdhPUM3bV0Yo/xPSdPw6ML9KHs/bjB4bLA5HXlbvYXPOD5yASodGod+krjYx21xm1QmL8dCJQ== dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-dotall-regex@^7.8.3": +"@babel/plugin-transform-dotall-regex@^7.4.4", "@babel/plugin-transform-dotall-regex@^7.8.3": version "7.8.3" resolved "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz#c3c6ec5ee6125c6993c5cbca20dc8621a9ea7a6e" integrity sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw== @@ -647,15 +560,7 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.8.3" "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-flow-strip-types@7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.8.3.tgz#da705a655466b2a9b36046b57bf0cbcd53551bd4" - integrity sha512-g/6WTWG/xbdd2exBBzMfygjX/zw4eyNC4X8pRaq7aRHRoDUCzAIu3kGYIXviOv8BjCuWm8vDBwjHcjiRNgXrPA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-flow" "^7.8.3" - -"@babel/plugin-transform-flow-strip-types@^7.9.0": +"@babel/plugin-transform-flow-strip-types@7.9.0", "@babel/plugin-transform-flow-strip-types@^7.9.0": version "7.9.0" resolved "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.9.0.tgz#8a3538aa40434e000b8f44a3c5c9ac7229bd2392" integrity sha512-7Qfg0lKQhEHs93FChxVLAvhBshOPQDtJUTVHr/ZwQNRccCm4O9D79r9tVSoV8iNwjP1YgfD+e/fgHcPkN1qEQg== @@ -663,10 +568,10 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-flow" "^7.8.3" -"@babel/plugin-transform-for-of@^7.8.4", "@babel/plugin-transform-for-of@^7.8.6": - version "7.8.6" - resolved "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.8.6.tgz#a051bd1b402c61af97a27ff51b468321c7c2a085" - integrity sha512-M0pw4/1/KI5WAxPsdcUL/w2LJ7o89YHN3yLkzNjg7Yl15GlVGgzHyCU+FMeAxevHGsLVmUqbirlUIKTafPmzdw== +"@babel/plugin-transform-for-of@^7.9.0": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.9.0.tgz#0f260e27d3e29cd1bb3128da5e76c761aa6c108e" + integrity sha512-lTAnWOpMwOXpyDx06N+ywmF3jNbafZEqZ96CGYabxHrxNX8l5ny7dt4bK/rGwAh9utyP2b2Hv7PlZh1AAS54FQ== dependencies: "@babel/helper-plugin-utils" "^7.8.3" @@ -692,41 +597,41 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-modules-amd@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.8.3.tgz#65606d44616b50225e76f5578f33c568a0b876a5" - integrity sha512-MadJiU3rLKclzT5kBH4yxdry96odTUwuqrZM+GllFI/VhxfPz+k9MshJM+MwhfkCdxxclSbSBbUGciBngR+kEQ== +"@babel/plugin-transform-modules-amd@^7.9.0": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.9.0.tgz#19755ee721912cf5bb04c07d50280af3484efef4" + integrity sha512-vZgDDF003B14O8zJy0XXLnPH4sg+9X5hFBBGN1V+B2rgrB+J2xIypSN6Rk9imB2hSTHQi5OHLrFWsZab1GMk+Q== dependencies: - "@babel/helper-module-transforms" "^7.8.3" + "@babel/helper-module-transforms" "^7.9.0" "@babel/helper-plugin-utils" "^7.8.3" babel-plugin-dynamic-import-node "^2.3.0" -"@babel/plugin-transform-modules-commonjs@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.8.3.tgz#df251706ec331bd058a34bdd72613915f82928a5" - integrity sha512-JpdMEfA15HZ/1gNuB9XEDlZM1h/gF/YOH7zaZzQu2xCFRfwc01NXBMHHSTT6hRjlXJJs5x/bfODM3LiCk94Sxg== +"@babel/plugin-transform-modules-commonjs@^7.9.0": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.9.0.tgz#e3e72f4cbc9b4a260e30be0ea59bdf5a39748940" + integrity sha512-qzlCrLnKqio4SlgJ6FMMLBe4bySNis8DFn1VkGmOcxG9gqEyPIOzeQrA//u0HAKrWpJlpZbZMPB1n/OPa4+n8g== dependencies: - "@babel/helper-module-transforms" "^7.8.3" + "@babel/helper-module-transforms" "^7.9.0" "@babel/helper-plugin-utils" "^7.8.3" "@babel/helper-simple-access" "^7.8.3" babel-plugin-dynamic-import-node "^2.3.0" -"@babel/plugin-transform-modules-systemjs@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.8.3.tgz#d8bbf222c1dbe3661f440f2f00c16e9bb7d0d420" - integrity sha512-8cESMCJjmArMYqa9AO5YuMEkE4ds28tMpZcGZB/jl3n0ZzlsxOAi3mC+SKypTfT8gjMupCnd3YiXCkMjj2jfOg== +"@babel/plugin-transform-modules-systemjs@^7.9.0": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.9.0.tgz#e9fd46a296fc91e009b64e07ddaa86d6f0edeb90" + integrity sha512-FsiAv/nao/ud2ZWy4wFacoLOm5uxl0ExSQ7ErvP7jpoihLR6Cq90ilOFyX9UXct3rbtKsAiZ9kFt5XGfPe/5SQ== dependencies: "@babel/helper-hoist-variables" "^7.8.3" - "@babel/helper-module-transforms" "^7.8.3" + "@babel/helper-module-transforms" "^7.9.0" "@babel/helper-plugin-utils" "^7.8.3" babel-plugin-dynamic-import-node "^2.3.0" -"@babel/plugin-transform-modules-umd@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.8.3.tgz#592d578ce06c52f5b98b02f913d653ffe972661a" - integrity sha512-evhTyWhbwbI3/U6dZAnx/ePoV7H6OUG+OjiJFHmhr9FPn0VShjwC2kdxqIuQ/+1P50TMrneGzMeyMTFOjKSnAw== +"@babel/plugin-transform-modules-umd@^7.9.0": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.9.0.tgz#e909acae276fec280f9b821a5f38e1f08b480697" + integrity sha512-uTWkXkIVtg/JGRSIABdBoMsoIeoHQHPTL0Y2E7xf5Oj7sLqwVsNXOkNk0VJc7vF0IMBsPeikHxFjGe+qmwPtTQ== dependencies: - "@babel/helper-module-transforms" "^7.8.3" + "@babel/helper-module-transforms" "^7.9.0" "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-transform-named-capturing-groups-regex@^7.8.3": @@ -751,12 +656,12 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/helper-replace-supers" "^7.8.3" -"@babel/plugin-transform-parameters@^7.8.4": - version "7.8.4" - resolved "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.8.4.tgz#1d5155de0b65db0ccf9971165745d3bb990d77d3" - integrity sha512-IsS3oTxeTsZlE5KqzTbcC2sV0P9pXdec53SU+Yxv7o/6dvGM5AkTotQKhoSffhNgZ/dftsSiOoxy7evCYJXzVA== +"@babel/plugin-transform-parameters@^7.8.7": + version "7.8.8" + resolved "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.8.8.tgz#0381de466c85d5404565243660c4496459525daf" + integrity sha512-hC4Ld/Ulpf1psQciWWwdnUspQoQco2bMzSrwU6TmzRlvoYQe4rQFy9vnCZDTlVeCQj0JPfL+1RX0V8hCJvkgBA== dependencies: - "@babel/helper-call-delegate" "^7.8.3" + "@babel/helper-call-delegate" "^7.8.7" "@babel/helper-get-function-arity" "^7.8.3" "@babel/helper-plugin-utils" "^7.8.3" @@ -767,15 +672,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-react-constant-elements@^7.0.0": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.8.3.tgz#784c25294bddaad2323eb4ff0c9f4a3f6c87d6bc" - integrity sha512-glrzN2U+egwRfkNFtL34xIBYTxbbUF2qJTP8HD3qETBBqzAWSeNB821X0GjU06+dNpq/UyCIjI72FmGE5NNkQQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-react-constant-elements@^7.2.0", "@babel/plugin-transform-react-constant-elements@^7.6.3": +"@babel/plugin-transform-react-constant-elements@^7.0.0", "@babel/plugin-transform-react-constant-elements@^7.2.0", "@babel/plugin-transform-react-constant-elements@^7.6.3": version "7.9.0" resolved "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.9.0.tgz#a75abc936a3819edec42d3386d9f1c93f28d9d9e" integrity sha512-wXMXsToAUOxJuBBEHajqKLFWcCkOSLshTI2ChCFFj1zDd7od4IOxiwLCOObNUvOpkxLpjIuaIdBMmNt6ocCPAw== @@ -789,37 +686,47 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-react-jsx-self@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.8.3.tgz#c4f178b2aa588ecfa8d077ea80d4194ee77ed702" - integrity sha512-01OT7s5oa0XTLf2I8XGsL8+KqV9lx3EZV+jxn/L2LQ97CGKila2YMroTkCEIE0HV/FF7CMSRsIAybopdN9NTdg== +"@babel/plugin-transform-react-jsx-development@^7.9.0": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.9.0.tgz#3c2a130727caf00c2a293f0aed24520825dbf754" + integrity sha512-tK8hWKrQncVvrhvtOiPpKrQjfNX3DtkNLSX4ObuGcpS9p0QrGetKmlySIGR07y48Zft8WVgPakqd/bk46JrMSw== + dependencies: + "@babel/helper-builder-react-jsx-experimental" "^7.9.0" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-jsx" "^7.8.3" + +"@babel/plugin-transform-react-jsx-self@^7.9.0": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.9.0.tgz#f4f26a325820205239bb915bad8e06fcadabb49b" + integrity sha512-K2ObbWPKT7KUTAoyjCsFilOkEgMvFG+y0FqOl6Lezd0/13kMkkjHskVsZvblRPj1PHA44PrToaZANrryppzTvQ== dependencies: "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-jsx" "^7.8.3" -"@babel/plugin-transform-react-jsx-source@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.8.3.tgz#951e75a8af47f9f120db731be095d2b2c34920e0" - integrity sha512-PLMgdMGuVDtRS/SzjNEQYUT8f4z1xb2BAT54vM1X5efkVuYBf5WyGUMbpmARcfq3NaglIwz08UVQK4HHHbC6ag== +"@babel/plugin-transform-react-jsx-source@^7.9.0": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.9.0.tgz#89ef93025240dd5d17d3122294a093e5e0183de0" + integrity sha512-K6m3LlSnTSfRkM6FcRk8saNEeaeyG5k7AVkBU2bZK3+1zdkSED3qNdsWrUgQBeTVD2Tp3VMmerxVO2yM5iITmw== dependencies: "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-jsx" "^7.8.3" -"@babel/plugin-transform-react-jsx@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.8.3.tgz#4220349c0390fdefa505365f68c103562ab2fc4a" - integrity sha512-r0h+mUiyL595ikykci+fbwm9YzmuOrUBi0b+FDIKmi3fPQyFokWVEMJnRWHJPPQEjyFJyna9WZC6Viv6UHSv1g== +"@babel/plugin-transform-react-jsx@^7.9.1": + version "7.9.1" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.9.1.tgz#d03af29396a6dc51bfa24eefd8005a9fd381152a" + integrity sha512-+xIZ6fPoix7h57CNO/ZeYADchg1tFyX9NDsnmNFFua8e1JNPln156mzS+8AQe1On2X2GLlANHJWHIXbMCqWDkQ== dependencies: - "@babel/helper-builder-react-jsx" "^7.8.3" + "@babel/helper-builder-react-jsx" "^7.9.0" + "@babel/helper-builder-react-jsx-experimental" "^7.9.0" "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-jsx" "^7.8.3" -"@babel/plugin-transform-regenerator@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.3.tgz#b31031e8059c07495bf23614c97f3d9698bc6ec8" - integrity sha512-qt/kcur/FxrQrzFR432FGZznkVAjiyFtCOANjkAKwCbt465L6ZCiUQh2oMYGU3Wo8LRFJxNDFwWn106S5wVUNA== +"@babel/plugin-transform-regenerator@^7.8.7": + version "7.8.7" + resolved "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.7.tgz#5e46a0dca2bee1ad8285eb0527e6abc9c37672f8" + integrity sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA== dependencies: - regenerator-transform "^0.14.0" + regenerator-transform "^0.14.2" "@babel/plugin-transform-reserved-words@^7.8.3": version "7.8.3" @@ -828,10 +735,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-runtime@7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.8.3.tgz#c0153bc0a5375ebc1f1591cb7eea223adea9f169" - integrity sha512-/vqUt5Yh+cgPZXXjmaG9NT8aVfThKk7G4OqkVhrXqwsC5soMn/qTCxs36rZ2QFhpfTJcjw4SNDIZ4RUb8OL4jQ== +"@babel/plugin-transform-runtime@7.9.0": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.9.0.tgz#45468c0ae74cc13204e1d3b1f4ce6ee83258af0b" + integrity sha512-pUu9VSf3kI1OqbWINQ7MaugnitRss1z533436waNXp+0N3ur3zfut37sXiQMxkuCF4VUjwZucen/quskCh7NHw== dependencies: "@babel/helper-module-imports" "^7.8.3" "@babel/helper-plugin-utils" "^7.8.3" @@ -875,10 +782,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-transform-typescript@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.8.3.tgz#be6f01a7ef423be68e65ace1f04fc407e6d88917" - integrity sha512-Ebj230AxcrKGZPKIp4g4TdQLrqX95TobLUWKd/CwG7X1XHUH1ZpkpFvXuXqWbtGRWb7uuEWNlrl681wsOArAdQ== +"@babel/plugin-transform-typescript@^7.9.0": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.9.0.tgz#8b52649c81cb7dee117f760952ab46675a258836" + integrity sha512-GRffJyCu16H3tEhbt9Q4buVFFBqrgS8FzTuhqSxlXNgmqD8aw2xmwtRwrvWXXlw7gHs664uqacsJymHJ9SUE/Q== dependencies: "@babel/helper-create-class-features-plugin" "^7.8.3" "@babel/helper-plugin-utils" "^7.8.3" @@ -892,27 +799,29 @@ "@babel/helper-create-regexp-features-plugin" "^7.8.3" "@babel/helper-plugin-utils" "^7.8.3" -"@babel/preset-env@7.8.4": - version "7.8.4" - resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.8.4.tgz#9dac6df5f423015d3d49b6e9e5fa3413e4a72c4e" - integrity sha512-HihCgpr45AnSOHRbS5cWNTINs0TwaR8BS8xIIH+QwiW8cKL0llV91njQMpeMReEPVs+1Ao0x3RLEBLtt1hOq4w== +"@babel/preset-env@7.9.0", "@babel/preset-env@^7.4.5": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.9.0.tgz#a5fc42480e950ae8f5d9f8f2bbc03f52722df3a8" + integrity sha512-712DeRXT6dyKAM/FMbQTV/FvRCms2hPCx+3weRjZ8iQVQWZejWWk1wwG6ViWMyqb/ouBbGOl5b6aCk0+j1NmsQ== dependencies: - "@babel/compat-data" "^7.8.4" - "@babel/helper-compilation-targets" "^7.8.4" + "@babel/compat-data" "^7.9.0" + "@babel/helper-compilation-targets" "^7.8.7" "@babel/helper-module-imports" "^7.8.3" "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-proposal-async-generator-functions" "^7.8.3" "@babel/plugin-proposal-dynamic-import" "^7.8.3" "@babel/plugin-proposal-json-strings" "^7.8.3" "@babel/plugin-proposal-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-proposal-object-rest-spread" "^7.8.3" + "@babel/plugin-proposal-numeric-separator" "^7.8.3" + "@babel/plugin-proposal-object-rest-spread" "^7.9.0" "@babel/plugin-proposal-optional-catch-binding" "^7.8.3" - "@babel/plugin-proposal-optional-chaining" "^7.8.3" + "@babel/plugin-proposal-optional-chaining" "^7.9.0" "@babel/plugin-proposal-unicode-property-regex" "^7.8.3" "@babel/plugin-syntax-async-generators" "^7.8.0" "@babel/plugin-syntax-dynamic-import" "^7.8.0" "@babel/plugin-syntax-json-strings" "^7.8.0" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + "@babel/plugin-syntax-numeric-separator" "^7.8.0" "@babel/plugin-syntax-object-rest-spread" "^7.8.0" "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" "@babel/plugin-syntax-optional-chaining" "^7.8.0" @@ -921,26 +830,26 @@ "@babel/plugin-transform-async-to-generator" "^7.8.3" "@babel/plugin-transform-block-scoped-functions" "^7.8.3" "@babel/plugin-transform-block-scoping" "^7.8.3" - "@babel/plugin-transform-classes" "^7.8.3" + "@babel/plugin-transform-classes" "^7.9.0" "@babel/plugin-transform-computed-properties" "^7.8.3" "@babel/plugin-transform-destructuring" "^7.8.3" "@babel/plugin-transform-dotall-regex" "^7.8.3" "@babel/plugin-transform-duplicate-keys" "^7.8.3" "@babel/plugin-transform-exponentiation-operator" "^7.8.3" - "@babel/plugin-transform-for-of" "^7.8.4" + "@babel/plugin-transform-for-of" "^7.9.0" "@babel/plugin-transform-function-name" "^7.8.3" "@babel/plugin-transform-literals" "^7.8.3" "@babel/plugin-transform-member-expression-literals" "^7.8.3" - "@babel/plugin-transform-modules-amd" "^7.8.3" - "@babel/plugin-transform-modules-commonjs" "^7.8.3" - "@babel/plugin-transform-modules-systemjs" "^7.8.3" - "@babel/plugin-transform-modules-umd" "^7.8.3" + "@babel/plugin-transform-modules-amd" "^7.9.0" + "@babel/plugin-transform-modules-commonjs" "^7.9.0" + "@babel/plugin-transform-modules-systemjs" "^7.9.0" + "@babel/plugin-transform-modules-umd" "^7.9.0" "@babel/plugin-transform-named-capturing-groups-regex" "^7.8.3" "@babel/plugin-transform-new-target" "^7.8.3" "@babel/plugin-transform-object-super" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.8.4" + "@babel/plugin-transform-parameters" "^7.8.7" "@babel/plugin-transform-property-literals" "^7.8.3" - "@babel/plugin-transform-regenerator" "^7.8.3" + "@babel/plugin-transform-regenerator" "^7.8.7" "@babel/plugin-transform-reserved-words" "^7.8.3" "@babel/plugin-transform-shorthand-properties" "^7.8.3" "@babel/plugin-transform-spread" "^7.8.3" @@ -948,71 +857,9 @@ "@babel/plugin-transform-template-literals" "^7.8.3" "@babel/plugin-transform-typeof-symbol" "^7.8.4" "@babel/plugin-transform-unicode-regex" "^7.8.3" - "@babel/types" "^7.8.3" - browserslist "^4.8.5" - core-js-compat "^3.6.2" - invariant "^2.2.2" - levenary "^1.1.1" - semver "^5.5.0" - -"@babel/preset-env@^7.4.5": - version "7.8.6" - resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.8.6.tgz#2a0773b08589ecba4995fc71b1965e4f531af40b" - integrity sha512-M5u8llV9DIVXBFB/ArIpqJuvXpO+ymxcJ6e8ZAmzeK3sQeBNOD1y+rHvHCGG4TlEmsNpIrdecsHGHT8ZCoOSJg== - dependencies: - "@babel/compat-data" "^7.8.6" - "@babel/helper-compilation-targets" "^7.8.6" - "@babel/helper-module-imports" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-proposal-async-generator-functions" "^7.8.3" - "@babel/plugin-proposal-dynamic-import" "^7.8.3" - "@babel/plugin-proposal-json-strings" "^7.8.3" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-proposal-object-rest-spread" "^7.8.3" - "@babel/plugin-proposal-optional-catch-binding" "^7.8.3" - "@babel/plugin-proposal-optional-chaining" "^7.8.3" - "@babel/plugin-proposal-unicode-property-regex" "^7.8.3" - "@babel/plugin-syntax-async-generators" "^7.8.0" - "@babel/plugin-syntax-dynamic-import" "^7.8.0" - "@babel/plugin-syntax-json-strings" "^7.8.0" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" - "@babel/plugin-syntax-object-rest-spread" "^7.8.0" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" - "@babel/plugin-syntax-optional-chaining" "^7.8.0" - "@babel/plugin-syntax-top-level-await" "^7.8.3" - "@babel/plugin-transform-arrow-functions" "^7.8.3" - "@babel/plugin-transform-async-to-generator" "^7.8.3" - "@babel/plugin-transform-block-scoped-functions" "^7.8.3" - "@babel/plugin-transform-block-scoping" "^7.8.3" - "@babel/plugin-transform-classes" "^7.8.6" - "@babel/plugin-transform-computed-properties" "^7.8.3" - "@babel/plugin-transform-destructuring" "^7.8.3" - "@babel/plugin-transform-dotall-regex" "^7.8.3" - "@babel/plugin-transform-duplicate-keys" "^7.8.3" - "@babel/plugin-transform-exponentiation-operator" "^7.8.3" - "@babel/plugin-transform-for-of" "^7.8.6" - "@babel/plugin-transform-function-name" "^7.8.3" - "@babel/plugin-transform-literals" "^7.8.3" - "@babel/plugin-transform-member-expression-literals" "^7.8.3" - "@babel/plugin-transform-modules-amd" "^7.8.3" - "@babel/plugin-transform-modules-commonjs" "^7.8.3" - "@babel/plugin-transform-modules-systemjs" "^7.8.3" - "@babel/plugin-transform-modules-umd" "^7.8.3" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.8.3" - "@babel/plugin-transform-new-target" "^7.8.3" - "@babel/plugin-transform-object-super" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.8.4" - "@babel/plugin-transform-property-literals" "^7.8.3" - "@babel/plugin-transform-regenerator" "^7.8.3" - "@babel/plugin-transform-reserved-words" "^7.8.3" - "@babel/plugin-transform-shorthand-properties" "^7.8.3" - "@babel/plugin-transform-spread" "^7.8.3" - "@babel/plugin-transform-sticky-regex" "^7.8.3" - "@babel/plugin-transform-template-literals" "^7.8.3" - "@babel/plugin-transform-typeof-symbol" "^7.8.4" - "@babel/plugin-transform-unicode-regex" "^7.8.3" - "@babel/types" "^7.8.6" - browserslist "^4.8.5" + "@babel/preset-modules" "^0.1.3" + "@babel/types" "^7.9.0" + browserslist "^4.9.1" core-js-compat "^3.6.2" invariant "^2.2.2" levenary "^1.1.1" @@ -1026,39 +873,59 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-transform-flow-strip-types" "^7.9.0" -"@babel/preset-react@7.8.3", "@babel/preset-react@^7.0.0": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.8.3.tgz#23dc63f1b5b0751283e04252e78cf1d6589273d2" - integrity sha512-9hx0CwZg92jGb7iHYQVgi0tOEHP/kM60CtWJQnmbATSPIQQ2xYzfoCI3EdqAhFBeeJwYMdWQuDUHMsuDbH9hyQ== +"@babel/preset-modules@^0.1.3": + version "0.1.3" + resolved "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.3.tgz#13242b53b5ef8c883c3cf7dddd55b36ce80fbc72" + integrity sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" + "@babel/plugin-transform-dotall-regex" "^7.4.4" + "@babel/types" "^7.4.4" + esutils "^2.0.2" + +"@babel/preset-react@7.9.1", "@babel/preset-react@^7.0.0": + version "7.9.1" + resolved "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.9.1.tgz#b346403c36d58c3bb544148272a0cefd9c28677a" + integrity sha512-aJBYF23MPj0RNdp/4bHnAP0NVqqZRr9kl0NAOP4nJCex6OYVio59+dnQzsAWFuogdLyeaKA1hmfUIVZkY5J+TQ== dependencies: "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-transform-react-display-name" "^7.8.3" - "@babel/plugin-transform-react-jsx" "^7.8.3" - "@babel/plugin-transform-react-jsx-self" "^7.8.3" - "@babel/plugin-transform-react-jsx-source" "^7.8.3" + "@babel/plugin-transform-react-jsx" "^7.9.1" + "@babel/plugin-transform-react-jsx-development" "^7.9.0" + "@babel/plugin-transform-react-jsx-self" "^7.9.0" + "@babel/plugin-transform-react-jsx-source" "^7.9.0" -"@babel/preset-typescript@7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.8.3.tgz#90af8690121beecd9a75d0cc26c6be39d1595d13" - integrity sha512-qee5LgPGui9zQ0jR1TeU5/fP9L+ovoArklEqY12ek8P/wV5ZeM/VYSQYwICeoT6FfpJTekG9Ilay5PhwsOpMHA== +"@babel/preset-typescript@7.9.0": + version "7.9.0" + resolved "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.9.0.tgz#87705a72b1f0d59df21c179f7c3d2ef4b16ce192" + integrity sha512-S4cueFnGrIbvYJgwsVFKdvOmpiL0XGw9MFW9D0vgRys5g36PBhZRL8NX8Gr2akz8XRtzq6HuDXPD/1nniagNUg== dependencies: "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-transform-typescript" "^7.8.3" + "@babel/plugin-transform-typescript" "^7.9.0" -"@babel/runtime@7.8.4", "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.3": - version "7.8.4" - resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.4.tgz#d79f5a2040f7caa24d53e563aad49cbc05581308" - integrity sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ== +"@babel/runtime-corejs3@^7.7.4", "@babel/runtime-corejs3@^7.8.3": + version "7.9.2" + resolved "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.9.2.tgz#26fe4aa77e9f1ecef9b776559bbb8e84d34284b7" + integrity sha512-HHxmgxbIzOfFlZ+tdeRKtaxWOMUoCG5Mu3wKeUmOxjYrwb3AAHgnmtCUbPPK11/raIWLIBK250t8E2BPO0p7jA== dependencies: - regenerator-runtime "^0.13.2" + core-js-pure "^3.0.0" + regenerator-runtime "^0.13.4" -"@babel/runtime@^7.5.0", "@babel/runtime@^7.7.4", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.7": +"@babel/runtime@7.9.0": version "7.9.0" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.0.tgz#337eda67401f5b066a6f205a3113d4ac18ba495b" integrity sha512-cTIudHnzuWLS56ik4DnRnqqNf8MkdUzV4iFFI1h7Jo9xvrpQROYaAnaSd2mHLQAzzZAPfATynX5ord6YlNYNMA== dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.4", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": + version "7.9.2" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz#d90df0583a3a252f09aaa619665367bae518db06" + integrity sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.4.0", "@babel/template@^7.7.4", "@babel/template@^7.8.3", "@babel/template@^7.8.6": version "7.8.6" resolved "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b" @@ -1068,22 +935,7 @@ "@babel/parser" "^7.8.6" "@babel/types" "^7.8.6" -"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.4.3", "@babel/traverse@^7.7.4", "@babel/traverse@^7.8.3", "@babel/traverse@^7.8.4", "@babel/traverse@^7.8.6": - version "7.8.6" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz#acfe0c64e1cd991b3e32eae813a6eb564954b5ff" - integrity sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A== - dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.8.6" - "@babel/helper-function-name" "^7.8.3" - "@babel/helper-split-export-declaration" "^7.8.3" - "@babel/parser" "^7.8.6" - "@babel/types" "^7.8.6" - debug "^4.1.0" - globals "^11.1.0" - lodash "^4.17.13" - -"@babel/traverse@^7.9.0": +"@babel/traverse@^7.1.0", "@babel/traverse@^7.4.3", "@babel/traverse@^7.7.0", "@babel/traverse@^7.7.4", "@babel/traverse@^7.8.3", "@babel/traverse@^7.8.6", "@babel/traverse@^7.9.0": version "7.9.0" resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.0.tgz#d3882c2830e513f4fe4cec9fe76ea1cc78747892" integrity sha512-jAZQj0+kn4WTHO5dUZkZKhbFrqZE7K5LAQ5JysMnmvGij+wOdr+8lWqPeW0BcF4wFwrEXXtdGO7wcV6YPJcf3w== @@ -1098,16 +950,7 @@ globals "^11.1.0" lodash "^4.17.13" -"@babel/types@^7.0.0", "@babel/types@^7.3.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.8.3", "@babel/types@^7.8.6": - version "7.8.6" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.8.6.tgz#629ecc33c2557fcde7126e58053127afdb3e6d01" - integrity sha512-wqz7pgWMIrht3gquyEFPVXeXCti72Rm8ep9b5tQKz9Yg9LzJA3HxosF1SB3Kc81KD1A3XBkkVYtJvCKS2Z/QrA== - dependencies: - esutils "^2.0.2" - lodash "^4.17.13" - to-fast-properties "^2.0.0" - -"@babel/types@^7.9.0": +"@babel/types@^7.0.0", "@babel/types@^7.3.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.7.0", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.8.7", "@babel/types@^7.9.0": version "7.9.0" resolved "https://registry.npmjs.org/@babel/types/-/types-7.9.0.tgz#00b064c3df83ad32b2dbf5ff07312b15c7f1efb5" integrity sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng== @@ -1300,16 +1143,11 @@ "@emotion/utils" "0.11.3" babel-plugin-emotion "^10.0.27" -"@emotion/hash@0.8.0": +"@emotion/hash@0.8.0", "@emotion/hash@^0.8.0": version "0.8.0" resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== -"@emotion/hash@^0.7.4": - version "0.7.4" - resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.7.4.tgz#f14932887422c9056b15a8d222a9074a7dfa2831" - integrity sha512-fxfMSBMX3tlIbKUdtGKxqB1fyrH6gVrX39Gsv3y8lRYKUqlgDt3UMqQyGnR1bQMa2B8aGnhLZokZgg8vT0Le+A== - "@emotion/is-prop-valid@0.8.8": version "0.8.8" resolved "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a" @@ -2508,15 +2346,15 @@ rimraf "^2.5.2" "@material-ui/core@^4.9.1": - version "4.9.4" - resolved "https://registry.npmjs.org/@material-ui/core/-/core-4.9.4.tgz#796515b12845dc6ea7e21872888cfc4c0c1c1efe" - integrity sha512-1wqm3jBC8mGpVHu0wVOYBX7LUzkPsWxkkTtKSn0Hz66T6TDJkke72mkSIL7akNdjnxy+bRc2Vi6NiJ4YutkDcw== + version "4.9.7" + resolved "https://registry.npmjs.org/@material-ui/core/-/core-4.9.7.tgz#0c1caf123278770f34c5d8e9ecd9e1314f87a621" + integrity sha512-RTRibZgq572GHEskMAG4sP+bt3P3XyIkv3pOTR8grZAW2rSUd6JoGZLRM4S2HkuO7wS7cAU5SpU2s1EsmTgWog== dependencies: "@babel/runtime" "^7.4.4" - "@material-ui/styles" "^4.9.0" - "@material-ui/system" "^4.9.3" + "@material-ui/styles" "^4.9.6" + "@material-ui/system" "^4.9.6" "@material-ui/types" "^5.0.0" - "@material-ui/utils" "^4.7.1" + "@material-ui/utils" "^4.9.6" "@types/react-transition-group" "^4.2.0" clsx "^1.0.2" hoist-non-react-statics "^3.3.2" @@ -2543,18 +2381,18 @@ prop-types "^15.7.2" react-is "^16.8.0" -"@material-ui/styles@^4.9.0": - version "4.9.0" - resolved "https://registry.npmjs.org/@material-ui/styles/-/styles-4.9.0.tgz#10c31859f6868cfa9d3adf6b6c3e32c9d676bc76" - integrity sha512-nJHum4RqYBPWsjL/9JET8Z02FZ9gSizlg/7LWVFpIthNzpK6OQ5OSRR4T4x9/p+wK3t1qNn3b1uI4XpnZaPxOA== +"@material-ui/styles@^4.9.6": + version "4.9.6" + resolved "https://registry.npmjs.org/@material-ui/styles/-/styles-4.9.6.tgz#924a30bf7c9b91af9c8f19c12c8573b8a4ecd085" + integrity sha512-ijgwStEkw1OZ6gCz18hkjycpr/3lKs1hYPi88O/AUn4vMuuGEGAIrqKVFq/lADmZUNF3DOFIk8LDkp7zmjPxtA== dependencies: "@babel/runtime" "^7.4.4" - "@emotion/hash" "^0.7.4" + "@emotion/hash" "^0.8.0" "@material-ui/types" "^5.0.0" - "@material-ui/utils" "^4.7.1" + "@material-ui/utils" "^4.9.6" clsx "^1.0.2" csstype "^2.5.2" - hoist-non-react-statics "^3.2.1" + hoist-non-react-statics "^3.3.2" jss "^10.0.3" jss-plugin-camel-case "^10.0.3" jss-plugin-default-unit "^10.0.3" @@ -2565,13 +2403,13 @@ jss-plugin-vendor-prefixer "^10.0.3" prop-types "^15.7.2" -"@material-ui/system@^4.9.3": - version "4.9.3" - resolved "https://registry.npmjs.org/@material-ui/system/-/system-4.9.3.tgz#ee48990d7941237fdaf21b7b399981d614bb0875" - integrity sha512-DBGsTKYrLlFpHG8BUp0X6ZpvaOzef+GhSwn/8DwVTXUdHitphaPQoL9xucrI8X9MTBo//El+7nylko7lo7eJIw== +"@material-ui/system@^4.9.6": + version "4.9.6" + resolved "https://registry.npmjs.org/@material-ui/system/-/system-4.9.6.tgz#fd060540224da4d1740da8ca6e7af288e217717e" + integrity sha512-QtfoAePyqXoZ2HUVSwGb1Ro0kucMCvVjbI0CdYIR21t0Opgfm1Oer6ni9P5lfeXA39xSt0wCierw37j+YES48Q== dependencies: "@babel/runtime" "^7.4.4" - "@material-ui/utils" "^4.7.1" + "@material-ui/utils" "^4.9.6" prop-types "^15.7.2" "@material-ui/types@^5.0.0": @@ -2579,10 +2417,10 @@ resolved "https://registry.npmjs.org/@material-ui/types/-/types-5.0.0.tgz#26d6259dc6b39f4c2e1e9aceff7a11e031941741" integrity sha512-UeH2BuKkwDndtMSS0qgx1kCzSMw+ydtj0xx/XbFtxNSTlXydKwzs5gVW5ZKsFlAkwoOOQ9TIsyoCC8hq18tOwg== -"@material-ui/utils@^4.7.1": - version "4.7.1" - resolved "https://registry.npmjs.org/@material-ui/utils/-/utils-4.7.1.tgz#dc16c7f0d2cd02fbcdd5cfe601fd6863ae3cc652" - integrity sha512-+ux0SlLdlehvzCk2zdQ3KiS3/ylWvuo/JwAGhvb8dFVvwR21K28z0PU9OQW2PGogrMEdvX3miEI5tGxTwwWiwQ== +"@material-ui/utils@^4.7.1", "@material-ui/utils@^4.9.6": + version "4.9.6" + resolved "https://registry.npmjs.org/@material-ui/utils/-/utils-4.9.6.tgz#5f1f9f6e4df9c8b6a263293b68c94834248ff157" + integrity sha512-gqlBn0JPPTUZeAktn1rgMcy9Iczrr74ecx31tyZLVGdBGGzsxzM6PP6zeS7FuoLS6vG4hoZP7hWnOoHtkR0Kvw== dependencies: "@babel/runtime" "^7.4.4" prop-types "^15.7.2" @@ -2672,9 +2510,9 @@ "@octokit/types" "^2.0.1" "@octokit/plugin-paginate-rest@^2.0.0": - version "2.0.1" - resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.0.1.tgz#8df1092b1f5442898be82aad6bf29681255574f9" - integrity sha512-xtW3AQoGDD0un/AkPjIndTdFO+O/My0I15TArvrbJirBCV91R1ElrE3gRcsUJENP3t/vveiQ9C6XQjo9sS2xQg== + version "2.0.2" + resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.0.2.tgz#fee7a81a4cc7d03784aaf9225499dd6e27f6d01e" + integrity sha512-HzODcSUt9mjErly26TlTOGZrhf9bmF/FEDQ2zln1izhgmIV6ulsjsHmgmR4VZ0wzVr/m52Eb6U2XuyS8fkcR1A== dependencies: "@octokit/types" "^2.0.1" @@ -2691,10 +2529,10 @@ "@octokit/types" "^2.0.1" deprecation "^2.3.1" -"@octokit/plugin-rest-endpoint-methods@^3.0.0": - version "3.2.0" - resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-3.2.0.tgz#ecc4bc594a57ebfb418b8c4a8c0f200455759004" - integrity sha512-k+RLsegQn4s0wvAFYuk3R18FVKRg3ktvzIGW6MkmrSiSXBwYfaEsv4CuPysyef0DL+74DRj/X9MLJYlbleUO+Q== +"@octokit/plugin-rest-endpoint-methods@^3.3.0": + version "3.3.1" + resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-3.3.1.tgz#279920ee391bd7e944ae4aa7fa435ba0c51fbb6a" + integrity sha512-iLAXPLWBZaP6ocy1GFfZUCzyN4cwg3y2JE6yZjQo0zLE3UaewC3TI68/TnS4ilyhXDxh81Jr1qwPN1AqTp8t3w== dependencies: "@octokit/types" "^2.0.1" deprecation "^2.3.1" @@ -2745,19 +2583,19 @@ universal-user-agent "^4.0.0" "@octokit/rest@^17.0.0": - version "17.0.0" - resolved "https://registry.npmjs.org/@octokit/rest/-/rest-17.0.0.tgz#1f44d96005f5946665fd42a85cd3e428172f01dc" - integrity sha512-nSlmyy1DBEOsC4voRbk/SN56V/iuZfxZzjFFz+ocb2MAYwHC+z1TyVOMV9W630dVn9ukioJO34VD5NSYwcgFWg== + version "17.1.1" + resolved "https://registry.npmjs.org/@octokit/rest/-/rest-17.1.1.tgz#357a9f6da687fc2ca9276715c1541562bd2c1581" + integrity sha512-Cn9XpevTJdQj/GbACY1WjArDabPpdAhvlS5zoCdZ/chiKbl4vutL1X1VeJHzDLK0K6BdZXXe4SnEPN31wKPA7A== dependencies: "@octokit/core" "^2.4.0" "@octokit/plugin-paginate-rest" "^2.0.0" "@octokit/plugin-request-log" "^1.0.0" - "@octokit/plugin-rest-endpoint-methods" "^3.0.0" + "@octokit/plugin-rest-endpoint-methods" "^3.3.0" "@octokit/types@^2.0.0", "@octokit/types@^2.0.1": - version "2.3.1" - resolved "https://registry.npmjs.org/@octokit/types/-/types-2.3.1.tgz#40cd61c125a6161cfb3bfabc75805ac7a54213b4" - integrity sha512-rvJP1Y9A/+Cky2C3var1vsw3Lf5Rjn/0sojNl2AjCX+WbpIHYccaJ46abrZoIxMYnOToul6S9tPytUVkFI7CXQ== + version "2.5.0" + resolved "https://registry.npmjs.org/@octokit/types/-/types-2.5.0.tgz#f1bbd147e662ae2c79717d518aac686e58257773" + integrity sha512-KEnLwOfdXzxPNL34fj508bhi9Z9cStyN7qY1kOfVahmqtAfrWw6Oq3P4R+dtsg0lYtZdWBpUrS/Ixmd5YILSww== dependencies: "@types/node" ">= 8" @@ -2797,9 +2635,9 @@ integrity sha512-9Tj/qn+y2j+sjCI3Jd+qseGtHjOAeg7dU2/lVcqIQ9TV3QDaDXDYXcoOHU+7o2Hwh8L8ymL4gfuO7KxDs3q2zg== "@semantic-release/github@^7.0.0": - version "7.0.4" - resolved "https://registry.npmjs.org/@semantic-release/github/-/github-7.0.4.tgz#6a62c020d086b84e723e143c78b4d72078e87d28" - integrity sha512-qQi41eGIa/tne7T8rvQK+xJNoyadOmd5mVsNZUUqZCVueiUkCItspJ7Mgy5ZWuhwlo28+hKeT/4zJ6MIG6er2Q== + version "7.0.5" + resolved "https://registry.npmjs.org/@semantic-release/github/-/github-7.0.5.tgz#042b515cbae8695aa60bc4ed17722c34512a5b89" + integrity sha512-1nJCMeomspRIXKiFO3VXtkUMbIBEreYLFNBdWoLjvlUNcEK0/pEbupEZJA3XHfJuSzv43u3OLpPhF/JBrMuv+A== dependencies: "@octokit/rest" "^17.0.0" "@semantic-release/error" "^2.2.0" @@ -2807,7 +2645,7 @@ bottleneck "^2.18.1" debug "^4.0.0" dir-glob "^3.0.0" - fs-extra "^8.0.0" + fs-extra "^9.0.0" globby "^11.0.0" http-proxy-agent "^4.0.0" https-proxy-agent "^5.0.0" @@ -2819,14 +2657,14 @@ url-join "^4.0.0" "@semantic-release/npm@^7.0.0": - version "7.0.3" - resolved "https://registry.npmjs.org/@semantic-release/npm/-/npm-7.0.3.tgz#0831f837c8e63cafd3c8a9e052b5aaf99a5a4921" - integrity sha512-3wOXMtAdJkaAnW5183iSmWSimtUmOx7m6g/DWPYRs2Gq6iyB+ztMmhgwbn6luNcM9t6pGbgHvRPEXpWkygMxCA== + version "7.0.5" + resolved "https://registry.npmjs.org/@semantic-release/npm/-/npm-7.0.5.tgz#61c45691abb863f6939cca6aac958d3c22508632" + integrity sha512-D+oEmsx9aHE1q806NFQwSC9KdBO8ri/VO99eEz0wWbX2jyLqVyWr7t0IjKC8aSnkkQswg/4KN/ZjfF6iz1XOpw== dependencies: "@semantic-release/error" "^2.2.0" aggregate-error "^3.0.0" execa "^4.0.0" - fs-extra "^8.0.0" + fs-extra "^9.0.0" lodash "^4.17.15" nerf-dart "^1.0.0" normalize-url "^5.0.0" @@ -2835,7 +2673,7 @@ read-pkg "^5.0.0" registry-auth-token "^4.0.0" semver "^7.1.2" - tempy "^0.4.0" + tempy "^0.5.0" "@semantic-release/release-notes-generator@^9.0.0": version "9.0.1" @@ -2854,9 +2692,9 @@ read-pkg-up "^7.0.0" "@sheerun/mutationobserver-shim@^0.3.2": - version "0.3.2" - resolved "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz#8013f2af54a2b7d735f71560ff360d3a8176a87b" - integrity sha512-vTCdPp/T/Q3oSqwHmZ5Kpa9oI7iLtGl3RQaA/NyLHikvcrPxACkkKVr/XzkSPJWXHRhKGzVvb0urJsbMlRxi1Q== + version "0.3.3" + resolved "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz#5405ee8e444ed212db44e79351f0c70a582aae25" + integrity sha512-DetpxZw1fzPD5xUBrIAoplLChO2VB8DlL5Gg+I1IR9b2wPqYIca2WSUxL5g1vLeR4MsQq1NeWriXAVffV+U1Fw== "@sinonjs/commons@^1.7.0": version "1.7.1" @@ -2865,10 +2703,10 @@ dependencies: type-detect "4.0.8" -"@spotify/eslint-config-base@^6.0.0": - version "6.0.0" - resolved "https://registry.npmjs.org/@spotify/eslint-config-base/-/eslint-config-base-6.0.0.tgz#487da7dbb89380b4eb778f4b902b8bba9b1f389f" - integrity sha512-R5NrJY/6hUWceiymcFia83j54yeB2SBeNYV3gZZuagSqI765358I3TEzccsS5ARo2zkPGKqfvawSP3ePVGCOMQ== +"@spotify/eslint-config-base@^6.1.0": + version "6.1.0" + resolved "https://registry.npmjs.org/@spotify/eslint-config-base/-/eslint-config-base-6.1.0.tgz#b48d2764049d56a7d83a95709ec1e6733cb19f2a" + integrity sha512-oxR3OBBrms09Ih6yPrqcaeKgwchdw9XgRvKsF6x6uPiMy0OVYald/+GkBtyQy+IChD14WStV/XosqNxEZeTJ4Q== "@spotify/eslint-config-react@^6.0.0": version "6.0.0" @@ -2880,12 +2718,12 @@ resolved "https://registry.npmjs.org/@spotify/eslint-config-typescript/-/eslint-config-typescript-6.0.0.tgz#8ae8469d2a0e219d71abaf2da14f8659c3912a34" integrity sha512-cK1iHhfMgvZFPANqLoChhgUQ1oqH+qTWILGvcsc6HylzVt9/kM0SyjGcDuZKj+CP6TecJODdyHgb5U6U+O5gKw== -"@spotify/eslint-config@^6.0.0": - version "6.0.0" - resolved "https://registry.npmjs.org/@spotify/eslint-config/-/eslint-config-6.0.0.tgz#45c535c302e02fc8bb459287930a729e9770a60c" - integrity sha512-KYQY8w8QuDz2IPJxh70xRQPd73RAJrwQVPCbkUBUzKb+0EBNx+kjX8JFBa7KNsJlnIu648f3XWYTySMFxGyFhQ== +"@spotify/eslint-config@^6.1.0": + version "6.1.0" + resolved "https://registry.npmjs.org/@spotify/eslint-config/-/eslint-config-6.1.0.tgz#67ef537c4419ecf275f790a79ce988820b3f5a95" + integrity sha512-pRfRFkW9XMHrzSErzzUxMRs/e+IL0i0o8crgMJJgk0J56+f0rQaXWkeDrI9HCw1uCkXLEwUoNPe+Rdd6cDk/lA== dependencies: - "@spotify/eslint-config-base" "^6.0.0" + "@spotify/eslint-config-base" "^6.1.0" "@spotify/eslint-config-react" "^6.0.0" "@spotify/eslint-config-typescript" "^6.0.0" "@spotify/web-scripts-utils" "^6.0.0" @@ -2915,13 +2753,13 @@ read-pkg-up "^7.0.1" "@spotify/web-scripts@^6.0.0": - version "6.0.0" - resolved "https://registry.npmjs.org/@spotify/web-scripts/-/web-scripts-6.0.0.tgz#577fc896adc11afe48e89671cb878ba4b6e14f29" - integrity sha512-JHw2sjkV3hECn/pGaioiMPwE9Xr0JGe/Mv91kxeV5ipL8ojKJt+5TSMVBdOVvg7jbgtHJ3JKgi/zh3ZHidl1bg== + version "6.1.0" + resolved "https://registry.npmjs.org/@spotify/web-scripts/-/web-scripts-6.1.0.tgz#3fb52aa9c77c4724e056a33e5b5994b327594278" + integrity sha512-1QVbd7HtIlYLIuNwR7Er/S9p3HLmXOFZAwmRLQ0xe67G1/gqIQOXpuMuembWLyTqi15efMrpNBlmvH64eW7nsw== dependencies: "@commitlint/cli" "^8.3.3" "@commitlint/config-conventional" "^8.3.3" - "@spotify/eslint-config" "^6.0.0" + "@spotify/eslint-config" "^6.1.0" "@spotify/prettier-config" "^6.0.0" "@spotify/tsconfig" "^6.0.0" "@spotify/web-scripts-utils" "^6.0.0" @@ -2937,11 +2775,12 @@ debug "^4.1.1" eslint "^6.8.0" jest "^25.1.0" + jest-config "^25.1.0" jest-junit "^10.0.0" lint-staged "^10.0.4" prettier "^1.18.2" semantic-release "^17.0.1" - ts-jest "^25.0.0" + ts-jest "^25.2.1" typescript "^3.7.4" "@storybook/addon-actions@^5.3.17": @@ -3402,17 +3241,18 @@ "@svgr/plugin-svgo" "^4.3.1" loader-utils "^1.2.3" -"@testing-library/dom@^6.11.0": - version "6.12.2" - resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-6.12.2.tgz#5d549acf43f2e0c23b2abfd4e36d65594c3b2741" - integrity sha512-KCnvHra5fV+wDxg3wJObGvZFxq7v1DJt829GNFLuRDjKxVNc/B5AdsylNF5PMHFbWMXDsHwM26d2NZcZO9KjbQ== +"@testing-library/dom@^6.15.0": + version "6.16.0" + resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-6.16.0.tgz#04ada27ed74ad4c0f0d984a1245bb29b1fd90ba9" + integrity sha512-lBD88ssxqEfz0wFL6MeUyyWZfV/2cjEZZV3YRpb2IoJRej/4f1jB0TzqIOznTpfR1r34CNesrubxwIlAQ8zgPA== dependencies: - "@babel/runtime" "^7.6.2" + "@babel/runtime" "^7.8.4" "@sheerun/mutationobserver-shim" "^0.3.2" - "@types/testing-library__dom" "^6.0.0" - aria-query "3.0.0" - pretty-format "^24.9.0" - wait-for-expect "^3.0.0" + "@types/testing-library__dom" "^6.12.1" + aria-query "^4.0.2" + dom-accessibility-api "^0.3.0" + pretty-format "^25.1.0" + wait-for-expect "^3.0.2" "@testing-library/jest-dom@^4.2.4": version "4.2.4" @@ -3430,12 +3270,12 @@ redent "^3.0.0" "@testing-library/react@^9.3.2": - version "9.4.1" - resolved "https://registry.npmjs.org/@testing-library/react/-/react-9.4.1.tgz#955771568aa3216107d307bfdf470bf2394308a0" - integrity sha512-sta3ui24HPgW92quHyQj6gpOkNgLNx8BX/QOU4k1bddo43ZdqlGwmzCYwL93bExfhergwiau+IzBGl7TCsSFeA== + version "9.5.0" + resolved "https://registry.npmjs.org/@testing-library/react/-/react-9.5.0.tgz#71531655a7890b61e77a1b39452fbedf0472ca5e" + integrity sha512-di1b+D0p+rfeboHO5W7gTVeZDIK5+maEgstrZbWZSSvxDyfDRkkyBE1AJR5Psd6doNldluXlCWqXriUfqu/9Qg== dependencies: - "@babel/runtime" "^7.8.3" - "@testing-library/dom" "^6.11.0" + "@babel/runtime" "^7.8.4" + "@testing-library/dom" "^6.15.0" "@types/testing-library__react" "^9.1.2" "@testing-library/user-event@^7.1.2": @@ -3495,9 +3335,9 @@ "@types/node" "*" "@types/classnames@^2.2.9": - version "2.2.9" - resolved "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.9.tgz#d868b6febb02666330410fe7f58f3c4b8258be7b" - integrity sha512-MNl+rT5UmZeilaPxAVs6YaPC2m6aA8rofviZbhbxpPpl61uKodfdQVsBtgJGTqGizEf02oW3tsVe7FYB8kK14A== + version "2.2.10" + resolved "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.10.tgz#cc658ca319b6355399efc1f5b9e818f1a24bf999" + integrity sha512-1UzDldn9GfYYEsWWnn/P4wkTlkZDH7lDb0wBMGbtIQc9zXEQq7FlKBdZUn6OBqD8sKZZ2RQO2mAjGpXiDGoRmQ== "@types/clean-css@*": version "4.2.1" @@ -3552,9 +3392,9 @@ "@types/json-schema" "*" "@types/estree@*": - version "0.0.42" - resolved "https://registry.npmjs.org/@types/estree/-/estree-0.0.42.tgz#8d0c1f480339efedb3e46070e22dd63e0430dd11" - integrity sha512-K1DPVvnBCPxzD+G51/cxVIoc2X8uUVl1zpJeE6iKcgHMj4+tbat5Xu4TjV7v2QSDbIeAfLi2hIk+u2+s0MlpUQ== + version "0.0.44" + resolved "https://registry.npmjs.org/@types/estree/-/estree-0.0.44.tgz#980cc5a29a3ef3bea6ff1f7d021047d7ea575e21" + integrity sha512-iaIVzr+w2ZJ5HkidlZ3EJM8VTZb2MJLCjw3V+505yVts0gRC4UMvjw0d1HPtGqI/HQC/KdsYtayfzl+AXY2R8g== "@types/events@*": version "3.0.0" @@ -3570,9 +3410,9 @@ "@types/range-parser" "*" "@types/express@*": - version "4.17.2" - resolved "https://registry.npmjs.org/@types/express/-/express-4.17.2.tgz#a0fb7a23d8855bac31bc01d5a58cadd9b2173e6c" - integrity sha512-5mHFNyavtLoJmnusB8OKJ5bshSzw+qkMIBAobLrIM48HJvunFva9mOa6aBwh64lBFyNwBbs0xiEFuj4eU/NjCA== + version "4.17.3" + resolved "https://registry.npmjs.org/@types/express/-/express-4.17.3.tgz#38e4458ce2067873b09a73908df488870c303bd9" + integrity sha512-I8cGRJj3pyOLs/HndoP+25vOqhqWkAZsWMEmq1qXy/b/M3ppufecUwaK2/TVDVxcV61/iSdhykUjQQ2DLSrTdg== dependencies: "@types/body-parser" "*" "@types/express-serve-static-core" "*" @@ -3632,9 +3472,9 @@ "@types/node" "*" "@types/http-proxy@*": - version "1.17.3" - resolved "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.3.tgz#348e1b808ff9585423cb909e9992d89ccdbf4c14" - integrity sha512-wIPqXANye5BbORbuh74exbwNzj+UWCwWyeEFJzUQ7Fq3W2NSAy+7x7nX1fgbEypr2/TdKqpeuxLnXWgzN533/Q== + version "1.17.4" + resolved "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.4.tgz#e7c92e3dbe3e13aa799440ff42e6d3a17a9d045b" + integrity sha512-IrSHl2u6AWXduUaDLqYpt45tLVCtYv7o4Z0s1KghBCDgIIS9oW5K1H8mZG/A2CfeLdEa7rTd1ACOiHBc1EMT2Q== dependencies: "@types/node" "*" @@ -3671,7 +3511,7 @@ "@types/istanbul-lib-coverage" "*" "@types/istanbul-lib-report" "*" -"@types/jest@*": +"@types/jest@*", "@types/jest@^25.1.0": version "25.1.4" resolved "https://registry.npmjs.org/@types/jest/-/jest-25.1.4.tgz#9e9f1e59dda86d3fd56afce71d1ea1b331f6f760" integrity sha512-QDDY2uNAhCV7TMCITrxz+MRk1EizcsevzfeS6LykIlq2V1E5oO4wXG8V2ZEd9w7Snxeeagk46YbMgZ8ESHx3sw== @@ -3686,14 +3526,6 @@ dependencies: jest-diff "^24.3.0" -"@types/jest@^25.1.0": - version "25.1.3" - resolved "https://registry.npmjs.org/@types/jest/-/jest-25.1.3.tgz#9b0b5addebccfb631175870be8ba62182f1bc35a" - integrity sha512-jqargqzyJWgWAJCXX96LBGR/Ei7wQcZBvRv0PLEu9ZByMfcs23keUJrKv9FMR6YZf9YCbfqDqgmY+JUBsnqhrg== - dependencies: - jest-diff "^25.1.0" - pretty-format "^25.1.0" - "@types/js-cookie@2.2.5": version "2.2.5" resolved "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.5.tgz#38dfaacae8623b37cc0b0d27398e574e3fc28b1e" @@ -3715,14 +3547,14 @@ integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== "@types/node@*", "@types/node@>= 8", "@types/node@^13.7.2": - version "13.7.6" - resolved "https://registry.npmjs.org/@types/node/-/node-13.7.6.tgz#cb734a7c191472ae6a2b3a502b4dfffcea974113" - integrity sha512-eyK7MWD0R1HqVTp+PtwRgFeIsemzuj4gBFSQxfPHY5iMjS7474e5wq+VFgTcdpyHeNxyKSaetYAjdMLJlKoWqA== + version "13.9.2" + resolved "https://registry.npmjs.org/@types/node/-/node-13.9.2.tgz#ace1880c03594cc3e80206d96847157d8e7fa349" + integrity sha512-bnoqK579sAYrQbp73wwglccjJ4sfRdKU7WNEZ5FW4K2U6Kc0/eZ5kvXG0JKsEKFB50zrFmfFt52/cvBbZa7eXg== "@types/node@^12.0.0": - version "12.12.28" - resolved "https://registry.npmjs.org/@types/node/-/node-12.12.28.tgz#3a2b5f8d21f96ace690a8832ae9779114612575f" - integrity sha512-g73GJYJDXgf0jqg+P9S8h2acWbDXNkoCX8DLtJVu7Fkn788pzQ/oJsrdJz/2JejRf/SjfZaAhsw+3nd1D5EWGg== + version "12.12.30" + resolved "https://registry.npmjs.org/@types/node/-/node-12.12.30.tgz#3501e6f09b954de9c404671cefdbcc5d9d7c45f6" + integrity sha512-sz9MF/zk6qVr3pAnM0BSQvYIBK44tS75QC5N+VbWSE4DjCV/pJ+UzCW/F+vVnl7TkOPcuwQureKNtSSwjBTaMg== "@types/normalize-package-data@^2.4.0": version "2.4.0" @@ -3826,9 +3658,9 @@ "@types/react" "*" "@types/react@*", "@types/react@^16.8.19": - version "16.9.23" - resolved "https://registry.npmjs.org/@types/react/-/react-16.9.23.tgz#1a66c6d468ba11a8943ad958a8cb3e737568271c" - integrity sha512-SsGVT4E7L2wLN3tPYLiF20hmZTPGuzaayVunfgXzUn1x4uHVsKH6QDJQ/TdpHqwsTLd4CwrmQ2vOgxN7gE24gw== + version "16.9.25" + resolved "https://registry.npmjs.org/@types/react/-/react-16.9.25.tgz#6ae2159b40138c792058a23c3c04fd3db49e929e" + integrity sha512-Dlj2V72cfYLPNscIG3/SMUOzhzj7GK3bpSrfefwt2YT9GLynvLCCZjbhyF6VsT0q0+aRACRX03TDJGb7cA0cqg== dependencies: "@types/prop-types" "*" csstype "^2.2.0" @@ -3873,10 +3705,10 @@ resolved "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.5.tgz#9adbc12950582aa65ead76bffdf39fe0c27a3c02" integrity sha512-/gG2M/Imw7cQFp8PGvz/SwocNrmKFjFsm5Pb8HdbHkZ1K8pmuPzOX4VeVoiEecFCVf4CsN1r3/BRvx+6sNqwtQ== -"@types/testing-library__dom@*", "@types/testing-library__dom@^6.0.0": - version "6.12.1" - resolved "https://registry.npmjs.org/@types/testing-library__dom/-/testing-library__dom-6.12.1.tgz#37af28fae051f9e3feed5684535b1540c97ae28b" - integrity sha512-cgqnEjxKk31tQt29j4baSWaZPNjQf3bHalj2gcHQTpW5SuHRal76gOpF0vypeEo6o+sS5inOvvNdzLY0B3FB2A== +"@types/testing-library__dom@*", "@types/testing-library__dom@^6.12.1": + version "6.14.0" + resolved "https://registry.npmjs.org/@types/testing-library__dom/-/testing-library__dom-6.14.0.tgz#1aede831cb4ed4a398448df5a2c54b54a365644e" + integrity sha512-sMl7OSv0AvMOqn1UJ6j1unPMIHRXen0Ita1ujnMX912rrOcawe4f7wu0Zt9GIQhBhJvH2BaibqFgQ3lP+Pj2hA== dependencies: pretty-format "^24.3.0" @@ -3888,12 +3720,13 @@ "@types/jest" "*" "@types/testing-library__react@^9.1.2": - version "9.1.2" - resolved "https://registry.npmjs.org/@types/testing-library__react/-/testing-library__react-9.1.2.tgz#e33af9124c60a010fc03a34eff8f8a34a75c4351" - integrity sha512-CYaMqrswQ+cJACy268jsLAw355DZtPZGt3Jwmmotlcu8O/tkoXBI6AeZ84oZBJsIsesozPKzWzmv/0TIU+1E9Q== + version "9.1.3" + resolved "https://registry.npmjs.org/@types/testing-library__react/-/testing-library__react-9.1.3.tgz#35eca61cc6ea923543796f16034882a1603d7302" + integrity sha512-iCdNPKU3IsYwRK9JieSYAiX0+aYDXOGAmrC/3/M7AqqSDKnWWVv07X+Zk1uFSL7cMTUYzv4lQRfohucEocn5/w== dependencies: "@types/react-dom" "*" "@types/testing-library__dom" "*" + pretty-format "^25.1.0" "@types/through@*": version "0.0.30" @@ -3910,9 +3743,9 @@ source-map "^0.6.1" "@types/webpack-dev-server@*", "@types/webpack-dev-server@^3.10.0": - version "3.10.0" - resolved "https://registry.npmjs.org/@types/webpack-dev-server/-/webpack-dev-server-3.10.0.tgz#5121ed285357b3b7463cac0d35e9b93819cf2167" - integrity sha512-ct/g/4WEEqOpXPqGX31TlZSzF1Sb7LXIViBz0oBSdH08XtNgY/hq/faXkw0yTxqvj7HJwS8dy2ggzqHposf1fQ== + version "3.10.1" + resolved "https://registry.npmjs.org/@types/webpack-dev-server/-/webpack-dev-server-3.10.1.tgz#93b7133cc9dab1ca1b76659f5ef8b763ad54c28a" + integrity sha512-2nwwQ/qHRghUirvG/gEDkOQDa+d881UTJM7EG9ok5KNaYCjYVvy7fdaO528Lcym9OQDn75SvruPYVVvMJxqO0g== dependencies: "@types/connect-history-api-fallback" "*" "@types/express" "*" @@ -3935,9 +3768,9 @@ source-map "^0.6.1" "@types/webpack@*", "@types/webpack@^4.41.7": - version "4.41.7" - resolved "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.7.tgz#22be27dbd4362b01c3954ca9b021dbc9328d9511" - integrity sha512-OQG9viYwO0V1NaNV7d0n79V+n6mjOV30CwgFPIfTzwmk8DHbt+C4f2aBGdCYbo3yFyYD6sjXfqqOjwkl1j+ulA== + version "4.41.8" + resolved "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.8.tgz#d2244f5f612ee30230a5c8c4ae678bce90d27277" + integrity sha512-mh4litLHTlDG84TGCFv1pZldndI34vkrW9Mks++Zx4KET7DRMoCXUvLbTISiuF4++fMgNnhV9cc1nCXJQyBYbQ== dependencies: "@types/anymatch" "*" "@types/node" "*" @@ -3970,83 +3803,40 @@ resolved "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.0.tgz#8b63ab7f1aa5321248aad5ac890a485656dcea4d" integrity sha512-te5lMAWii1uEJ4FwLjzdlbw3+n0FZNOvFXHxQDKeT0dilh7HOzdMzV2TrJVUzq8ep7J4Na8OUYPRLSQkJHAlrg== -"@typescript-eslint/eslint-plugin@^2.10.0": - version "2.22.0" - resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.22.0.tgz#218ce6d4aa0244c6a40baba39ca1e021b26bb017" - integrity sha512-BvxRLaTDVQ3N+Qq8BivLiE9akQLAOUfxNHIEhedOcg8B2+jY8Rc4/D+iVprvuMX1AdezFYautuGDwr9QxqSxBQ== +"@typescript-eslint/eslint-plugin@^2.10.0", "@typescript-eslint/eslint-plugin@^2.14.0": + version "2.24.0" + resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.24.0.tgz#a86cf618c965a462cddf3601f594544b134d6d68" + integrity sha512-wJRBeaMeT7RLQ27UQkDFOu25MqFOBus8PtOa9KaT5ZuxC1kAsd7JEHqWt4YXuY9eancX0GK9C68i5OROnlIzBA== dependencies: - "@typescript-eslint/experimental-utils" "2.22.0" + "@typescript-eslint/experimental-utils" "2.24.0" eslint-utils "^1.4.3" functional-red-black-tree "^1.0.1" regexpp "^3.0.0" tsutils "^3.17.1" -"@typescript-eslint/eslint-plugin@^2.14.0": - version "2.21.0" - resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.21.0.tgz#a34de84a0791cae0357c4dda805c5b4e8203b6c6" - integrity sha512-b5jjjDMxzcjh/Sbjuo7WyhrQmVJg0WipTHQgXh5Xwx10uYm6nPWqN1WGOsaNq4HR3Zh4wUx4IRQdDkCHwyewyw== - dependencies: - "@typescript-eslint/experimental-utils" "2.21.0" - eslint-utils "^1.4.3" - functional-red-black-tree "^1.0.1" - regexpp "^3.0.0" - tsutils "^3.17.1" - -"@typescript-eslint/experimental-utils@2.21.0", "@typescript-eslint/experimental-utils@^2.5.0": - version "2.21.0" - resolved "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.21.0.tgz#71de390a3ec00b280b69138d80733406e6e86bfa" - integrity sha512-olKw9JP/XUkav4lq0I7S1mhGgONJF9rHNhKFn9wJlpfRVjNo3PPjSvybxEldvCXnvD+WAshSzqH5cEjPp9CsBA== +"@typescript-eslint/experimental-utils@2.24.0", "@typescript-eslint/experimental-utils@^2.5.0": + version "2.24.0" + resolved "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.24.0.tgz#a5cb2ed89fedf8b59638dc83484eb0c8c35e1143" + integrity sha512-DXrwuXTdVh3ycNCMYmWhUzn/gfqu9N0VzNnahjiDJvcyhfBy4gb59ncVZVxdp5XzBC77dCncu0daQgOkbvPwBw== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/typescript-estree" "2.21.0" + "@typescript-eslint/typescript-estree" "2.24.0" eslint-scope "^5.0.0" -"@typescript-eslint/experimental-utils@2.22.0": - version "2.22.0" - resolved "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.22.0.tgz#4d00c91fbaaa68e56e7869be284999a265707f85" - integrity sha512-sJt1GYBe6yC0dWOQzXlp+tiuGglNhJC9eXZeC8GBVH98Zv9jtatccuhz0OF5kC/DwChqsNfghHx7OlIDQjNYAQ== - dependencies: - "@types/json-schema" "^7.0.3" - "@typescript-eslint/typescript-estree" "2.22.0" - eslint-scope "^5.0.0" - -"@typescript-eslint/parser@^2.10.0": - version "2.22.0" - resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.22.0.tgz#8eeb6cb6de873f655e64153397d4790898e149d0" - integrity sha512-FaZKC1X+nvD7qMPqKFUYHz3H0TAioSVFGvG29f796Nc5tBluoqfHgLbSFKsh7mKjRoeTm8J9WX2Wo9EyZWjG7w== +"@typescript-eslint/parser@^2.10.0", "@typescript-eslint/parser@^2.14.0": + version "2.24.0" + resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.24.0.tgz#2cf0eae6e6dd44d162486ad949c126b887f11eb8" + integrity sha512-H2Y7uacwSSg8IbVxdYExSI3T7uM1DzmOn2COGtCahCC3g8YtM1xYAPi2MAHyfPs61VKxP/J/UiSctcRgw4G8aw== dependencies: "@types/eslint-visitor-keys" "^1.0.0" - "@typescript-eslint/experimental-utils" "2.22.0" - "@typescript-eslint/typescript-estree" "2.22.0" + "@typescript-eslint/experimental-utils" "2.24.0" + "@typescript-eslint/typescript-estree" "2.24.0" eslint-visitor-keys "^1.1.0" -"@typescript-eslint/parser@^2.14.0": - version "2.21.0" - resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.21.0.tgz#4f200995517c3d5fc5ef51b17527bc948992e438" - integrity sha512-VrmbdrrrvvI6cPPOG7uOgGUFXNYTiSbnRq8ZMyuGa4+qmXJXVLEEz78hKuqupvkpwJQNk1Ucz1TenrRP90gmBg== - dependencies: - "@types/eslint-visitor-keys" "^1.0.0" - "@typescript-eslint/experimental-utils" "2.21.0" - "@typescript-eslint/typescript-estree" "2.21.0" - eslint-visitor-keys "^1.1.0" - -"@typescript-eslint/typescript-estree@2.21.0": - version "2.21.0" - resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.21.0.tgz#7e4be29f2e338195a2e8c818949ed0ff727cc943" - integrity sha512-NC/nogZNb9IK2MEFQqyDBAciOT8Lp8O3KgAfvHx2Skx6WBo+KmDqlU3R9KxHONaijfTIKtojRe3SZQyMjr3wBw== - dependencies: - debug "^4.1.1" - eslint-visitor-keys "^1.1.0" - glob "^7.1.6" - is-glob "^4.0.1" - lodash "^4.17.15" - semver "^6.3.0" - tsutils "^3.17.1" - -"@typescript-eslint/typescript-estree@2.22.0": - version "2.22.0" - resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.22.0.tgz#a16ed45876abf743e1f5857e2f4a1c3199fd219e" - integrity sha512-2HFZW2FQc4MhIBB8WhDm9lVFaBDy6h9jGrJ4V2Uzxe/ON29HCHBTj3GkgcsgMWfsl2U5as+pTOr30Nibaw7qRQ== +"@typescript-eslint/typescript-estree@2.24.0": + version "2.24.0" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.24.0.tgz#38bbc8bb479790d2f324797ffbcdb346d897c62a" + integrity sha512-RJ0yMe5owMSix55qX7Mi9V6z2FDuuDpN6eR5fzRJrp+8in9UF41IGNQHbg5aMK4/PjVaEQksLvz0IA8n+Mr/FA== dependencies: debug "^4.1.1" eslint-visitor-keys "^1.1.0" @@ -4202,10 +3992,10 @@ "@webassemblyjs/wast-parser" "1.8.5" "@xtuc/long" "4.2.2" -"@xobotyi/scrollbar-width@1.9.3": - version "1.9.3" - resolved "https://registry.npmjs.org/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.3.tgz#76a82f3a3452967cf05509e472c6e154804ba07d" - integrity sha512-15JdCA3BjDktOdJ9xRdxkZcd2ZiHHbb3ycSPtC9JCjwkHzvrPr3n8uZoNw9CYF6UhkIMnW85d+P9mi3eXaKcFg== +"@xobotyi/scrollbar-width@1.9.4": + version "1.9.4" + resolved "https://registry.npmjs.org/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.4.tgz#a7dce20b7465bcad29cd6bbb557695e4ea7863cb" + integrity sha512-o12FCQt/X5n3pgKEWGpt0f/7Eg4mfv3uRwPUrctiOT8ZuxbH3cNLGWfH/8y6KxVJg4L2885ucuXQ6XECZzUiJA== "@xtuc/ieee754@^1.2.0": version "1.2.0" @@ -4260,7 +4050,7 @@ acorn-globals@^4.1.0, acorn-globals@^4.3.0, acorn-globals@^4.3.2: acorn "^6.0.1" acorn-walk "^6.0.1" -acorn-jsx@^5.1.0: +acorn-jsx@^5.2.0: version "5.2.0" resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe" integrity sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ== @@ -4272,18 +4062,18 @@ acorn-walk@^6.0.1: acorn@^5.5.3: version "5.7.4" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e" + resolved "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e" integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg== acorn@^6.0.1, acorn@^6.0.4, acorn@^6.2.1: - version "6.4.0" - resolved "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz#b659d2ffbafa24baf5db1cdbb2c94a983ecd2784" - integrity sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw== + version "6.4.1" + resolved "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" + integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== -acorn@^7.1.0: - version "7.1.0" - resolved "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz#949d36f2c292535da602283586c2477c57eb2d6c" - integrity sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ== +acorn@^7.1.0, acorn@^7.1.1: + version "7.1.1" + resolved "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf" + integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg== address@1.1.2, address@^1.0.1: version "1.1.2" @@ -4410,11 +4200,11 @@ ansi-escapes@^3.0.0, ansi-escapes@^3.2.0: integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== ansi-escapes@^4.2.1, ansi-escapes@^4.3.0: - version "4.3.0" - resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz#a4ce2b33d6b214b7950d8595c212f12ac9cc569d" - integrity sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg== + version "4.3.1" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" + integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== dependencies: - type-fest "^0.8.1" + type-fest "^0.11.0" ansi-html@0.0.7: version "0.0.7" @@ -4549,7 +4339,7 @@ argv-formatter@~1.0.0: resolved "https://registry.npmjs.org/argv-formatter/-/argv-formatter-1.0.0.tgz#a0ca0cbc29a5b73e836eebe1cbf6c5e0e4eb82f9" integrity sha1-oMoMvCmltz6Dbuvhy/bF4OTrgvk= -aria-query@3.0.0, aria-query@^3.0.0: +aria-query@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz#65b3fcc1ca1155a8c9ae64d6eee297f15d5133cc" integrity sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w= @@ -4557,6 +4347,14 @@ aria-query@3.0.0, aria-query@^3.0.0: ast-types-flow "0.0.7" commander "^2.11.0" +aria-query@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/aria-query/-/aria-query-4.0.2.tgz#250687b4ccde1ab86d127da0432ae3552fc7b145" + integrity sha512-S1G1V790fTaigUSM/Gd0NngzEfiMy9uTUfMyHhKhVyy4cH5O/eTuR01ydhGL0z4Za1PXFTRGH3qL8VhUQuEO5w== + dependencies: + "@babel/runtime" "^7.7.4" + "@babel/runtime-corejs3" "^7.7.4" + arity-n@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/arity-n/-/arity-n-1.0.4.tgz#d9e76b11733e08569c0847ae7b39b2860b30b745" @@ -4758,6 +4556,11 @@ asynckit@^0.4.0: resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + atob-lite@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/atob-lite/-/atob-lite-2.0.0.tgz#0fef5ad46f1bd7a8502c65727f0367d5ee43d696" @@ -4805,15 +4608,15 @@ babel-code-frame@^6.22.0: esutils "^2.0.2" js-tokens "^3.0.2" -babel-eslint@10.0.3: - version "10.0.3" - resolved "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.3.tgz#81a2c669be0f205e19462fed2482d33e4687a88a" - integrity sha512-z3U7eMY6r/3f3/JB9mTsLjyxrv0Yb1zb8PCWCLpguxfCzBIZUwy23R1t/XKewP+8mEN2Ck8Dtr4q20z6ce6SoA== +babel-eslint@10.1.0: + version "10.1.0" + resolved "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" + integrity sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg== dependencies: "@babel/code-frame" "^7.0.0" - "@babel/parser" "^7.0.0" - "@babel/traverse" "^7.0.0" - "@babel/types" "^7.0.0" + "@babel/parser" "^7.7.0" + "@babel/traverse" "^7.7.0" + "@babel/types" "^7.7.0" eslint-visitor-keys "^1.0.0" resolve "^1.12.0" @@ -4885,17 +4688,7 @@ babel-jest@^25.1.0: chalk "^3.0.0" slash "^3.0.0" -babel-loader@8.0.6: - version "8.0.6" - resolved "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.6.tgz#e33bdb6f362b03f4bb141a0c21ab87c501b70dfb" - integrity sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw== - dependencies: - find-cache-dir "^2.0.0" - loader-utils "^1.0.2" - mkdirp "^0.5.1" - pify "^4.0.1" - -babel-loader@^8.1.0: +babel-loader@8.1.0, babel-loader@^8.1.0: version "8.1.0" resolved "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz#c611d5112bd5209abe8b9fa84c3e4da25275f1c3" integrity sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw== @@ -5205,22 +4998,24 @@ babel-preset-jest@^25.1.0: babel-plugin-transform-undefined-to-void "^6.9.4" lodash "^4.17.11" -babel-preset-react-app@^9.1.1: - version "9.1.1" - resolved "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-9.1.1.tgz#d1ceb47cbe48b285fdd5c562c54c432ed5a41e0e" - integrity sha512-YkWP2UwY//TLltNlEBRngDOrYhvSLb+CA330G7T9M5UhGEMWe+JK/8IXJc5p2fDTSfSiETf+PY0+PYXFMix81Q== +babel-preset-react-app@^9.1.2: + version "9.1.2" + resolved "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-9.1.2.tgz#54775d976588a8a6d1a99201a702befecaf48030" + integrity sha512-k58RtQOKH21NyKtzptoAvtAODuAJJs3ZhqBMl456/GnXEQ/0La92pNmwgWoMn5pBTrsvk3YYXdY7zpY4e3UIxA== dependencies: - "@babel/core" "7.8.4" + "@babel/core" "7.9.0" "@babel/plugin-proposal-class-properties" "7.8.3" "@babel/plugin-proposal-decorators" "7.8.3" + "@babel/plugin-proposal-nullish-coalescing-operator" "7.8.3" "@babel/plugin-proposal-numeric-separator" "7.8.3" - "@babel/plugin-transform-flow-strip-types" "7.8.3" + "@babel/plugin-proposal-optional-chaining" "7.9.0" + "@babel/plugin-transform-flow-strip-types" "7.9.0" "@babel/plugin-transform-react-display-name" "7.8.3" - "@babel/plugin-transform-runtime" "7.8.3" - "@babel/preset-env" "7.8.4" - "@babel/preset-react" "7.8.3" - "@babel/preset-typescript" "7.8.3" - "@babel/runtime" "7.8.4" + "@babel/plugin-transform-runtime" "7.9.0" + "@babel/preset-env" "7.9.0" + "@babel/preset-react" "7.9.1" + "@babel/preset-typescript" "7.9.0" + "@babel/runtime" "7.9.0" babel-plugin-macros "2.8.0" babel-plugin-transform-react-remove-prop-types "0.4.24" @@ -5437,10 +5232,10 @@ brorand@^1.0.1: resolved "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= -browser-process-hrtime@^0.1.2: - version "0.1.3" - resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz#616f00faef1df7ec1b5bf9cfe2bdc3170f26c7b4" - integrity sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw== +browser-process-hrtime@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" + integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== browser-resolve@^1.11.3: version "1.11.3" @@ -5508,6 +5303,16 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" +browserslist@4.10.0, browserslist@^4.0.0, browserslist@^4.6.2, browserslist@^4.6.4, browserslist@^4.8.3, browserslist@^4.9.1: + version "4.10.0" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.10.0.tgz#f179737913eaf0d2b98e4926ac1ca6a15cbcc6a9" + integrity sha512-TpfK0TDgv71dzuTsEAlQiHeWQ/tiPqgNZVdv046fvNtBZrjbv2O3TsWCDU0AWGJJKCF/KsjNdLzR9hXOsh/CfA== + dependencies: + caniuse-lite "^1.0.30001035" + electron-to-chromium "^1.3.378" + node-releases "^1.1.52" + pkg-up "^3.1.0" + browserslist@4.7.0: version "4.7.0" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.7.0.tgz#9ee89225ffc07db03409f2fee524dc8227458a17" @@ -5517,24 +5322,6 @@ browserslist@4.7.0: electron-to-chromium "^1.3.247" node-releases "^1.1.29" -browserslist@4.8.6: - version "4.8.6" - resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.8.6.tgz#96406f3f5f0755d272e27a66f4163ca821590a7e" - integrity sha512-ZHao85gf0eZ0ESxLfCp73GG9O/VTytYDIkIiZDlURppLTI9wErSM/5yAKEq6rcUdxBLjMELmrYUJGg5sxGKMHg== - dependencies: - caniuse-lite "^1.0.30001023" - electron-to-chromium "^1.3.341" - node-releases "^1.1.47" - -browserslist@^4.0.0, browserslist@^4.6.2, browserslist@^4.6.4, browserslist@^4.8.3, browserslist@^4.8.5: - version "4.9.0" - resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.9.0.tgz#ff85c390889e0f754d7bd8ad13412575cdcf5dc7" - integrity sha512-seffIXhwgB84+OCeT/aMjpZnsAsYDiMSC+CEs3UkF8iU64BZGYcu+TZYs/IBpo4nRi0vJywUJWYdbTsOhFTweg== - dependencies: - caniuse-lite "^1.0.30001030" - electron-to-chromium "^1.3.361" - node-releases "^1.1.50" - bs-logger@0.x: version "0.2.6" resolved "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" @@ -5775,12 +5562,7 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001020, caniuse-lite@^1.0.30001023, caniuse-lite@^1.0.30001030: - version "1.0.30001030" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001030.tgz#78076c4c6d67d3e41d6eb9399853fb27fe6e44ee" - integrity sha512-QGK0W4Ft/Ac+zTjEiRJfwDNATvS3fodDczBXrH42784kcfqcDKpEPfN08N0HQjrAp8He/Jw8QiSS9QRn7XAbUw== - -caniuse-lite@^1.0.30000989: +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30000989, caniuse-lite@^1.0.30001020, caniuse-lite@^1.0.30001035: version "1.0.30001035" resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001035.tgz#2bb53b8aa4716b2ed08e088d4dc816a5fe089a1e" integrity sha512-C1ZxgkuA4/bUEdMbU5WrGY4+UhMFFiXrgNAfxiMIqWgFTWfv/xsZCS2xEHT2LMq7xAZfuAnu6mcqyDl0ZR6wLQ== @@ -6560,7 +6342,7 @@ core-js-compat@^3.6.2: browserslist "^4.8.3" semver "7.0.0" -core-js-pure@^3.0.1: +core-js-pure@^3.0.0, core-js-pure@^3.0.1: version "3.6.4" resolved "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.4.tgz#4bf1ba866e25814f149d4e9aaa08c36173506e3a" integrity sha512-epIhRLkXdgv32xIUFaaAry2wdxZYBi6bgM7cB136dzzXXa+dFyRLTZeLUJxnd8ShrmyVXBub63n2NHo2JAt8Cw== @@ -6661,9 +6443,9 @@ create-react-context@0.3.0, create-react-context@^0.3.0: warning "^4.0.3" cross-env@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/cross-env/-/cross-env-7.0.0.tgz#5a3b2ddce51ec713ea58f2fb79ce22e65b4f5479" - integrity sha512-rV6M9ldNgmwP7bx5u6rZsTbYidzwvrwIYZnT08hSGLcQCcggofgFW+sNe7IhA1SRauPS0QuLbbX+wdNtpqE5CQ== + version "7.0.2" + resolved "https://registry.npmjs.org/cross-env/-/cross-env-7.0.2.tgz#bd5ed31339a93a3418ac4f3ca9ca3403082ae5f9" + integrity sha512-KZP/bMEOJEDCkDQAyRhu3RL2ZO/SUVrxQVI0G3YEQ+OLbRA3c6zgixe8Mq8a/z7+HKlNEjo8oiLUs8iRijY2Rw== dependencies: cross-spawn "^7.0.1" @@ -7410,6 +7192,11 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +dom-accessibility-api@^0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.3.0.tgz#511e5993dd673b97c87ea47dba0e3892f7e0c983" + integrity sha512-PzwHEmsRP3IGY4gv/Ug+rMeaTIyTJvadCb+ujYXYeIylbHJezIyNToe8KfEgHTCEYyC+/bUghYOGg8yMGlZ6vA== + dom-converter@^0.2: version "0.2.0" resolved "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" @@ -7596,16 +7383,11 @@ ejs@^2.7.4: resolved "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba" integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== -electron-to-chromium@^1.3.247: +electron-to-chromium@^1.3.247, electron-to-chromium@^1.3.378: version "1.3.380" resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.380.tgz#1e1f07091b42b54bccd0ad6d3a14f2b73b60dc9d" integrity sha512-2jhQxJKcjcSpVOQm0NAfuLq8o+130blrcawoumdXT6411xG/xIAOyZodO/y7WTaYlz/NHe3sCCAe/cJLnDsqTw== -electron-to-chromium@^1.3.341, electron-to-chromium@^1.3.361: - version "1.3.363" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.363.tgz#08756873e49446a92e0cee6c3cd9eb3c52043826" - integrity sha512-4w19wPBkeunBjOA53lNFT36IdOD3Tk1OoIDtTX+VToJUUDX42QfuhtsNKXv25wmSnoBOExM3kTbj7/WDNBwHuQ== - elegant-spinner@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" @@ -7699,9 +7481,9 @@ entities@^2.0.0: integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== env-ci@^5.0.0: - version "5.0.1" - resolved "https://registry.npmjs.org/env-ci/-/env-ci-5.0.1.tgz#f5904477d1e1ae7593110c133292171d8b1a5d6c" - integrity sha512-xXgohoOAFFF1Y3EdsSKP7olyH/DLS6ZD3aglV6mDFAXBqBXLJSsZLrOZdYfDs5mOmgNaP3YYynObzwF3QkC24g== + version "5.0.2" + resolved "https://registry.npmjs.org/env-ci/-/env-ci-5.0.2.tgz#48b6687f8af8cdf5e31b8fcf2987553d085249d9" + integrity sha512-Xc41mKvjouTXD3Oy9AqySz1IeyvJvHZ20Twf5ZLYbNpPPIuCnL/qHCmNlD01LoNy0JTunw9HPYVptD19Ac7Mbw== dependencies: execa "^4.0.0" java-properties "^1.0.0" @@ -7868,10 +7650,10 @@ eslint-config-prettier@^6.0.0: dependencies: get-stdin "^6.0.0" -eslint-config-react-app@^5.2.0: - version "5.2.0" - resolved "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-5.2.0.tgz#135110ba56a9e378f7acfe5f36e2ae76a2317899" - integrity sha512-WrHjoGpKr1kLLiWDD81tme9jMM0hk5cMxasLSdyno6DdPt+IfLOrDJBVo6jN7tn4y1nzhs43TmUaZWO6Sf0blw== +eslint-config-react-app@^5.2.1: + version "5.2.1" + resolved "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-5.2.1.tgz#698bf7aeee27f0cea0139eaef261c7bf7dd623df" + integrity sha512-pGIZ8t0mFLcV+6ZirRgYK6RVqUIKRIi9MmgzUEmrIknsn3AdO0I32asO86dJgloHq+9ZPl8UIg8mYrvgP5u2wQ== dependencies: confusing-browser-globals "^1.0.9" @@ -7909,10 +7691,10 @@ eslint-plugin-flowtype@4.6.0: dependencies: lodash "^4.17.15" -eslint-plugin-import@2.20.0: - version "2.20.0" - resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.20.0.tgz#d749a7263fb6c29980def8e960d380a6aa6aecaa" - integrity sha512-NK42oA0mUc8Ngn4kONOPsPB1XhbUvNHqF+g307dPV28aknPoiNnKLFd9em4nkswwepdF5ouieqv5Th/63U7YJQ== +eslint-plugin-import@2.20.1: + version "2.20.1" + resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.20.1.tgz#802423196dcb11d9ce8435a5fc02a6d3b46939b3" + integrity sha512-qQHgFOTjguR+LnYRoToeZWT62XM55MBVXObHM6SKFd1VzDcX/vqT1kAz8ssqigh5eMj8qXcRoXXGZpPP6RfdCw== dependencies: array-includes "^3.0.3" array.prototype.flat "^1.2.1" @@ -7928,9 +7710,9 @@ eslint-plugin-import@2.20.0: resolve "^1.12.0" eslint-plugin-jest@^23.6.0: - version "23.8.0" - resolved "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-23.8.0.tgz#c978f959fa8395801742d13c5a3125a986d2661e" - integrity sha512-DKXmLxguZ1Lru4u5YM12ko3WLq6gqo7dhV2b63K731+/PNyZ/Ff6NGONQsGUtPLG9zU3kdz/N+2LTbweNZifeg== + version "23.8.2" + resolved "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-23.8.2.tgz#6f28b41c67ef635f803ebd9e168f6b73858eb8d4" + integrity sha512-xwbnvOsotSV27MtAe7s8uGWOori0nUsrXh2f1EnpmXua8sDfY6VZhHAhHg2sqK7HBNycRQExF074XSZ7DvfoFg== dependencies: "@typescript-eslint/experimental-utils" "^2.5.0" @@ -7963,10 +7745,10 @@ eslint-plugin-react-hooks@^1.6.1: resolved "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.7.0.tgz#6210b6d5a37205f0b92858f895a4e827020a7d04" integrity sha512-iXTCFcOmlWvw4+TOE8CLWj6yX1GwzT0Y6cUfHHZqWnSk144VmVIRcVGtUAzrLES7C798lmvnt02C7rxaOX1HNA== -eslint-plugin-react@7.18.0: - version "7.18.0" - resolved "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.18.0.tgz#2317831284d005b30aff8afb7c4e906f13fa8e7e" - integrity sha512-p+PGoGeV4SaZRDsXqdj9OWcOrOpZn8gXoGPcIQTzo2IDMbAKhNDnME9myZWqO3Ic4R3YmwAZ1lDjWl2R2hMUVQ== +eslint-plugin-react@7.19.0, eslint-plugin-react@^7.12.4: + version "7.19.0" + resolved "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.19.0.tgz#6d08f9673628aa69c5559d33489e855d83551666" + integrity sha512-SPT8j72CGuAP+JFbT0sJHOB80TX/pu44gQ4vXH/cq+hQTiY2PuZ6IHkqXJV6x1b28GDdo1lbInjKUrrdUf0LOQ== dependencies: array-includes "^3.1.1" doctrine "^2.1.0" @@ -7976,23 +7758,10 @@ eslint-plugin-react@7.18.0: object.fromentries "^2.0.2" object.values "^1.1.1" prop-types "^15.7.2" - resolve "^1.14.2" - -eslint-plugin-react@^7.12.4: - version "7.18.3" - resolved "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.18.3.tgz#8be671b7f6be095098e79d27ac32f9580f599bc8" - integrity sha512-Bt56LNHAQCoou88s8ViKRjMB2+36XRejCQ1VoLj716KI1MoE99HpTVvIThJ0rvFmG4E4Gsq+UgToEjn+j044Bg== - dependencies: - array-includes "^3.1.1" - doctrine "^2.1.0" - has "^1.0.3" - jsx-ast-utils "^2.2.3" - object.entries "^1.1.1" - object.fromentries "^2.0.2" - object.values "^1.1.1" - prop-types "^15.7.2" - resolve "^1.14.2" + resolve "^1.15.1" + semver "^6.3.0" string.prototype.matchall "^4.0.2" + xregexp "^4.3.0" eslint-scope@^4.0.3: version "4.0.3" @@ -8066,12 +7835,12 @@ eslint@^6.6.0, eslint@^6.8.0: v8-compile-cache "^2.0.3" espree@^6.1.2: - version "6.1.2" - resolved "https://registry.npmjs.org/espree/-/espree-6.1.2.tgz#6c272650932b4f91c3714e5e7b5f5e2ecf47262d" - integrity sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA== + version "6.2.1" + resolved "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" + integrity sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw== dependencies: - acorn "^7.1.0" - acorn-jsx "^5.1.0" + acorn "^7.1.1" + acorn-jsx "^5.2.0" eslint-visitor-keys "^1.1.0" esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: @@ -8098,7 +7867,7 @@ estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== -esutils@^2.0.0, esutils@^2.0.2: +esutils@^2.0.2: version "2.0.3" resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== @@ -8552,7 +8321,7 @@ find-cache-dir@^0.1.1: mkdirp "^0.5.1" pkg-dir "^1.0.0" -find-cache-dir@^2.0.0, find-cache-dir@^2.1.0: +find-cache-dir@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== @@ -8725,9 +8494,9 @@ fork-ts-checker-webpack-plugin@3.1.1: worker-rpc "^0.1.0" fork-ts-checker-webpack-plugin@^4.0.5: - version "4.0.5" - resolved "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-4.0.5.tgz#1982b464cb8a44d224940a95a0d075c8bbd700bf" - integrity sha512-7juf7VGQ4GD7beQI2vsHlWME4H7cabXn4+zYzvWTOYQUjn3L+3tWQe8j9KnVOpdCf+1PoSvYMRT2A2kltfGLtQ== + version "4.1.0" + resolved "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-4.1.0.tgz#62bffe704426770fee33f15f0c0d56c86297fefd" + integrity sha512-2DLwUVUR/AdNmMD2utfmSR8r4qHRFhnfL6QQDQS5q4g5uBZzXYDgg8MXPIbu0HzyLjyvbogqjBNKILG5fufwzg== dependencies: babel-code-frame "^6.22.0" chalk "^2.4.1" @@ -8784,7 +8553,7 @@ from2@^2.1.0, from2@^2.3.0: inherits "^2.0.1" readable-stream "^2.0.0" -fs-extra@8.1.0, fs-extra@^8.0.0, fs-extra@^8.0.1, fs-extra@^8.1.0: +fs-extra@8.1.0, fs-extra@^8.0.1, fs-extra@^8.1.0: version "8.1.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== @@ -8822,6 +8591,16 @@ fs-extra@^7.0.0: jsonfile "^4.0.0" universalify "^0.1.0" +fs-extra@^9.0.0: + version "9.0.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.0.tgz#b6afc31036e247b2466dc99c29ae797d5d4580a3" + integrity sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^1.0.0" + fs-minipass@^1.2.5: version "1.2.7" resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" @@ -8866,9 +8645,9 @@ fsevents@2.1.2, fsevents@^2.1.2, fsevents@~2.1.2: integrity sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA== fsevents@^1.2.7: - version "1.2.11" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-1.2.11.tgz#67bf57f4758f02ede88fb2a1712fef4d15358be3" - integrity sha512-+ux3lx6peh0BpvY0JebGyZoiR4D+oYzdPZMKJwkZ+sFkNJzpL7tXc/wehS49gUAxg3tmMHPHZkA8JU2rhhgDHw== + version "1.2.12" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-1.2.12.tgz#db7e0d8ec3b0b45724fd4d83d43554a8f1f0de5c" + integrity sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q== dependencies: bindings "^1.5.0" nan "^2.12.1" @@ -9116,9 +8895,9 @@ glob-parent@^3.1.0: path-dirname "^1.0.0" glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0: - version "5.1.0" - resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2" - integrity sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw== + version "5.1.1" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== dependencies: is-glob "^4.0.1" @@ -9208,9 +8987,9 @@ globals@^11.1.0: integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^12.1.0: - version "12.3.0" - resolved "https://registry.npmjs.org/globals/-/globals-12.3.0.tgz#1e564ee5c4dded2ab098b0f88f24702a3c56be13" - integrity sha512-wAfjdLgFsPZsklLJvOBUBmzYE8/CwhEqSBEMRXA3qxIiNtyqvjYurAtIfDh6chlEPUfmTY3MnZh5Hfh4q0UlIw== + version "12.4.0" + resolved "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" + integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== dependencies: type-fest "^0.8.1" @@ -9503,7 +9282,7 @@ hoist-non-react-statics@^2.3.1: resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47" integrity sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw== -hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.2.1, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: +hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -9522,10 +9301,10 @@ hook-std@^2.0.0: resolved "https://registry.npmjs.org/hook-std/-/hook-std-2.0.0.tgz#ff9aafdebb6a989a354f729bb6445cf4a3a7077c" integrity sha512-zZ6T5WcuBMIUVh49iPQS9t977t7C0l7OtHrpeMb5uk48JdflRX0NSFvCekfYNmGQETnLq9W/isMyHl69kxGi8g== -hosted-git-info@^2.1.4, hosted-git-info@^2.7.1, hosted-git-info@^2.8.7: - version "2.8.7" - resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.7.tgz#4d2e0d5248e1cfabc984b0f6a6d75fe36e679511" - integrity sha512-ChkjQtKJ3GI6SsI4O5jwr8q8EPrWCnxuc4Tbx+vRI5x6mDOpjKKltNo1lRlszw3xwgTOSns1ZRBiMmmwpcvLxg== +hosted-git-info@^2.1.4, hosted-git-info@^2.7.1, hosted-git-info@^2.8.8: + version "2.8.8" + resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" + integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== hosted-git-info@^3.0.0: version "3.0.4" @@ -9572,9 +9351,9 @@ html-entities@^1.2.0, html-entities@^1.2.1: integrity sha1-DfKTUfByEWNRXfueVUPl9u7VFi8= html-escaper@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.0.tgz#71e87f931de3fe09e56661ab9a29aadec707b491" - integrity sha512-a4u9BeERWGu/S8JiWEAQcdrg9v4QArtP9keViQjGMdff20fBdd8waotXaNmODqBe6uZ3Nafi7K/ho4gCQHV3Ig== + version "2.0.1" + resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.1.tgz#beed86b5d2b921e92533aa11bce6d8e3b583dee7" + integrity sha512-hNX23TjWwD3q56HpWjUHOKj1+4KKlnjv9PcmBUYKVpga+2cnb9nDx/B1o0yO4n+RZXZdiNxzx6B24C9aNMTkkQ== html-minifier-terser@^5.0.1: version "5.0.4" @@ -10008,7 +9787,7 @@ inquirer@6.5.0: strip-ansi "^5.1.0" through "^2.3.6" -inquirer@7.0.4, inquirer@^7.0.0, inquirer@^7.0.4: +inquirer@7.0.4: version "7.0.4" resolved "https://registry.npmjs.org/inquirer/-/inquirer-7.0.4.tgz#99af5bde47153abca23f5c7fc30db247f39da703" integrity sha512-Bu5Td5+j11sCkqfqmUTiwv+tWisMtP0L7Q8WrqA2C/BbBhy1YTdFrvjjlrKq8oagA/tLQBski2Gcx/Sqyi2qSQ== @@ -10046,6 +9825,25 @@ inquirer@^6.2.0: strip-ansi "^5.1.0" through "^2.3.6" +inquirer@^7.0.0, inquirer@^7.0.4: + version "7.1.0" + resolved "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz#1298a01859883e17c7264b82870ae1034f92dd29" + integrity sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg== + dependencies: + ansi-escapes "^4.2.1" + chalk "^3.0.0" + cli-cursor "^3.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.15" + mute-stream "0.0.8" + run-async "^2.4.0" + rxjs "^6.5.3" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + internal-ip@^4.3.0: version "4.3.0" resolved "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" @@ -11661,12 +11459,12 @@ json3@^3.3.2: resolved "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA== -json5@2.x, json5@^2.1.0: - version "2.1.1" - resolved "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz#81b6cb04e9ba496f1c7005d07b4368a2638f90b6" - integrity sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ== +json5@2.x, json5@^2.1.1, json5@^2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/json5/-/json5-2.1.2.tgz#43ef1f0af9835dd624751a6b7fa48874fb2d608e" + integrity sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ== dependencies: - minimist "^1.2.0" + minimist "^1.2.5" json5@^0.5.0: version "0.5.1" @@ -11680,13 +11478,6 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" -json5@^2.1.1, json5@^2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/json5/-/json5-2.1.2.tgz#43ef1f0af9835dd624751a6b7fa48874fb2d608e" - integrity sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ== - dependencies: - minimist "^1.2.5" - jsonfile@^2.1.0: version "2.4.0" resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" @@ -11701,6 +11492,15 @@ jsonfile@^4.0.0: optionalDependencies: graceful-fs "^4.1.6" +jsonfile@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz#98966cba214378c8c84b82e085907b40bf614179" + integrity sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg== + dependencies: + universalify "^1.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + jsonify@~0.0.0: version "0.0.0" resolved "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" @@ -11722,68 +11522,68 @@ jsprim@^1.2.2: verror "1.10.0" jss-plugin-camel-case@^10.0.3: - version "10.0.4" - resolved "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.0.4.tgz#3dedecec1e5bba0bf6141c2c05e2ab11ea4b468d" - integrity sha512-+wnqxJsyfUnOn0LxVg3GgZBSjfBCrjxwx7LFxwVTUih0ceGaXKZoieheNOaTo5EM4w8bt1nbb8XonpQCj67C6A== + version "10.1.1" + resolved "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.1.1.tgz#8e73ecc4f1d0f8dfe4dd31f6f9f2782588970e78" + integrity sha512-MDIaw8FeD5uFz1seQBKz4pnvDLnj5vIKV5hXSVdMaAVq13xR6SVTVWkIV/keyTs5txxTvzGJ9hXoxgd1WTUlBw== dependencies: "@babel/runtime" "^7.3.1" hyphenate-style-name "^1.0.3" - jss "10.0.4" + jss "10.1.1" jss-plugin-default-unit@^10.0.3: - version "10.0.4" - resolved "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.0.4.tgz#df03885de20f20a1fc1c21bdb7c62e865ee400d9" - integrity sha512-T0mhL/Ogp/quvod/jAHEqKvptLDxq7Cj3a+7zRuqK8HxUYkftptN89wJElZC3rshhNKiogkEYhCWenpJdFvTBg== + version "10.1.1" + resolved "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.1.1.tgz#2df86016dfe73085eead843f5794e3890e9c5c47" + integrity sha512-UkeVCA/b3QEA4k0nIKS4uWXDCNmV73WLHdh2oDGZZc3GsQtlOCuiH3EkB/qI60v2MiCq356/SYWsDXt21yjwdg== dependencies: "@babel/runtime" "^7.3.1" - jss "10.0.4" + jss "10.1.1" jss-plugin-global@^10.0.3: - version "10.0.4" - resolved "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.0.4.tgz#412245b56133cc88bec654a70d82d5922619f4c5" - integrity sha512-N8n9/GHENZce+sqE4UYiZiJtI+t+erT/BypHOrNYAfIoNEj7OYsOEKfIo2P0GpLB3QyDAYf5eo9XNdZ8veEkUA== + version "10.1.1" + resolved "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.1.1.tgz#36b0d6d9facb74dfd99590643708a89260747d14" + integrity sha512-VBG3wRyi3Z8S4kMhm8rZV6caYBegsk+QnQZSVmrWw6GVOT/Z4FA7eyMu5SdkorDlG/HVpHh91oFN56O4R9m2VA== dependencies: "@babel/runtime" "^7.3.1" - jss "10.0.4" + jss "10.1.1" jss-plugin-nested@^10.0.3: - version "10.0.4" - resolved "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.0.4.tgz#4d15ad13995fb6e4125618006473a096d2475d75" - integrity sha512-QM21BKVt8LDeoRfowvAMh/s+/89VYrreIIE6ch4pvw0oAXDWw1iorUPlqLZ7uCO3UL0uFtQhJq3QMLN6Lr1v0A== + version "10.1.1" + resolved "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.1.1.tgz#5c3de2b8bda344de1ebcef3a4fd30870a29a8a8c" + integrity sha512-ozEu7ZBSVrMYxSDplPX3H82XHNQk2DQEJ9TEyo7OVTPJ1hEieqjDFiOQOxXEj9z3PMqkylnUbvWIZRDKCFYw5Q== dependencies: "@babel/runtime" "^7.3.1" - jss "10.0.4" + jss "10.1.1" tiny-warning "^1.0.2" jss-plugin-props-sort@^10.0.3: - version "10.0.4" - resolved "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.0.4.tgz#43c880ff8dfcf858f809f663ece5e65a1d945b5a" - integrity sha512-WoETdOCjGskuin/OMt2uEdDPLZF3vfQuHXF+XUHGJrq0BAapoyGQDcv37SeReDlkRAbVXkEZPsIMvYrgHSHFiA== + version "10.1.1" + resolved "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.1.1.tgz#34bddcbfaf9430ec8ccdf92729f03bb10caf1785" + integrity sha512-g/joK3eTDZB4pkqpZB38257yD4LXB0X15jxtZAGbUzcKAVUHPl9Jb47Y7lYmiGsShiV4YmQRqG1p2DHMYoK91g== dependencies: "@babel/runtime" "^7.3.1" - jss "10.0.4" + jss "10.1.1" jss-plugin-rule-value-function@^10.0.3: - version "10.0.4" - resolved "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.0.4.tgz#2f4cf4a86ad3eba875bb48cb9f4a7ed35cb354e7" - integrity sha512-0hrzOSWRF5ABJGaHrlnHbYZjU877Ofzfh2id3uLtBvemGQLHI+ldoL8/+6iPSRa7M8z8Ngfg2vfYhKjUA5gA0g== + version "10.1.1" + resolved "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.1.1.tgz#be00dac6fc394aaddbcef5860b9eca6224d96382" + integrity sha512-ClV1lvJ3laU9la1CUzaDugEcwnpjPTuJ0yGy2YtcU+gG/w9HMInD5vEv7xKAz53Bk4WiJm5uLOElSEshHyhKNw== dependencies: "@babel/runtime" "^7.3.1" - jss "10.0.4" + jss "10.1.1" jss-plugin-vendor-prefixer@^10.0.3: - version "10.0.4" - resolved "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.0.4.tgz#1626ef612a4541cff17cf96815e1740155214ed2" - integrity sha512-4JgEbcrdeMda1qvxTm1CnxFJAWVV++VLpP46HNTrfH7VhVlvUpihnUNs2gAlKuRT/XSBuiWeLAkrTqF4NVrPig== + version "10.1.1" + resolved "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.1.1.tgz#8348b20749f790beebab3b6a8f7075b07c2cfcfd" + integrity sha512-09MZpQ6onQrhaVSF6GHC4iYifQ7+4YC/tAP6D4ZWeZotvCMq1mHLqNKRIaqQ2lkgANjlEot2JnVi1ktu4+L4pw== dependencies: "@babel/runtime" "^7.3.1" css-vendor "^2.0.7" - jss "10.0.4" + jss "10.1.1" -jss@10.0.4, jss@^10.0.3: - version "10.0.4" - resolved "https://registry.npmjs.org/jss/-/jss-10.0.4.tgz#46ebdde1c40c9a079d64f3334cb88ae28fd90bfd" - integrity sha512-GqHmeDK83qbqMAVjxyPfN1qJVTKZne533a9bdCrllZukUM8npG/k+JumEPI86IIB5ifaZAHG2HAsUziyxOiooQ== +jss@10.1.1, jss@^10.0.3: + version "10.1.1" + resolved "https://registry.npmjs.org/jss/-/jss-10.1.1.tgz#450b27d53761af3e500b43130a54cdbe157ea332" + integrity sha512-Xz3qgRUFlxbWk1czCZibUJqhVPObrZHxY3FPsjCXhDld4NOj1BgM14Ir5hVm+Qr6OLqVljjGvoMcCdXNOAbdkQ== dependencies: "@babel/runtime" "^7.3.1" csstype "^2.6.5" @@ -12195,12 +11995,12 @@ load-json-file@^5.3.0: type-fest "^0.3.0" loader-fs-cache@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.2.tgz#54cedf6b727e1779fd8f01205f05f6e88706f086" - integrity sha512-70IzT/0/L+M20jUlEqZhZyArTU6VKLRTYRDAYN26g4jfzpJqjipLL3/hgYpySqI9PwsVRHHFja0LfEmsx9X2Cw== + version "1.0.3" + resolved "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.3.tgz#f08657646d607078be2f0a032f8bd69dd6f277d9" + integrity sha512-ldcgZpjNJj71n+2Mf6yetz+c9bM4xpKtNds4LbqXzU/PTdeAX0g3ytnU1AJMEcTk2Lex4Smpe3Q/eCTsvUBxbA== dependencies: find-cache-dir "^0.1.1" - mkdirp "0.5.1" + mkdirp "^0.5.1" loader-runner@^2.4.0: version "2.4.0" @@ -12618,9 +12418,9 @@ marked-terminal@^4.0.0: supports-hyperlinks "^2.0.0" marked@^0.8.0: - version "0.8.0" - resolved "https://registry.npmjs.org/marked/-/marked-0.8.0.tgz#ec5c0c9b93878dc52dd54be8d0e524097bd81a99" - integrity sha512-MyUe+T/Pw4TZufHkzAfDj6HarCBWia2y27/bhuYkTaiUnfDYFnCP3KUN+9oM7Wi6JA2rymtVYbQu3spE0GCmxQ== + version "0.8.1" + resolved "https://registry.npmjs.org/marked/-/marked-0.8.1.tgz#a233f39572fab15ede53a3c3be8a139bff86d2dd" + integrity sha512-tZfJS8uE0zpo7xpTffwFwYRfW9AzNcdo04Qcjs+C9+oCy8MSRD2reD5iDVtYx8mtLaqsGughw/YLlcwNxAHA1g== md5.js@^1.3.4: version "1.3.5" @@ -12913,17 +12713,12 @@ minimist-options@^3.0.1: arrify "^1.0.1" is-plain-obj "^1.1.0" -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= - -minimist@1.2.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0: +minimist@1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= -minimist@^1.2.5: +minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== @@ -13020,14 +12815,7 @@ mkdirp@*: resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.3.tgz#4cf2e30ad45959dddea53ad97d518b6c8205e1ea" integrity sha512-6uCP4Qc0sWsgMLy1EOqqS/3rjDHOEnsStVr/4vtAIK2Y5i2kA7lFFejYrpIyiN9w0pYf4ckeCYT9f1r1P9KX5g== -mkdirp@0.5.1, mkdirp@0.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: - version "0.5.1" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= - dependencies: - minimist "0.0.8" - -mkdirp@^0.5.3: +mkdirp@0.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@~0.5.0, mkdirp@~0.5.1: version "0.5.3" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz#5a514b7179259287952881e94410ec5465659f8c" integrity sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg== @@ -13209,9 +12997,9 @@ node-emoji@^1.10.0: lodash.toarray "^4.4.0" node-fetch-npm@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz#7258c9046182dca345b4208eda918daf33697ff7" - integrity sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw== + version "2.0.3" + resolved "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.3.tgz#efae4aacb0500444e449a51fc1467397775ebc38" + integrity sha512-DgwoKEsqLnFZtk3ap7GWBHcHwnUhsNmQqEDcdjfQ8GofLEFJ081NAd4Uin3R7RFZBWVJCwHISw1oaEqPgSLloA== dependencies: encoding "^0.1.11" json-parse-better-errors "^1.0.0" @@ -13235,7 +13023,7 @@ node-forge@0.9.0: resolved "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579" integrity sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ== -node-gyp@^5.0.2, node-gyp@^5.0.7: +node-gyp@^5.0.2, node-gyp@^5.1.0: version "5.1.0" resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-5.1.0.tgz#8e31260a7af4a2e2f994b0673d4e0b3866156332" integrity sha512-OUTryc5bt/P8zVgNUmC6xdXiDJxLMAW8cF5tLQOT9E5sOQj+UeQxnnPy74K3CLCa/SOjjBlbuzDLR8ANwA+wmw== @@ -13313,20 +13101,13 @@ node-notifier@^6.0.0: shellwords "^0.1.1" which "^1.3.1" -node-releases@^1.1.29: +node-releases@^1.1.29, node-releases@^1.1.52: version "1.1.52" resolved "https://registry.npmjs.org/node-releases/-/node-releases-1.1.52.tgz#bcffee3e0a758e92e44ecfaecd0a47554b0bcba9" integrity sha512-snSiT1UypkgGt2wxPqS6ImEUICbNCMb31yaxWrOLXjhlt2z2/IBpaOxzONExqSm4y5oLnAqjjRWu+wsDzK5yNQ== dependencies: semver "^6.3.0" -node-releases@^1.1.47, node-releases@^1.1.50: - version "1.1.50" - resolved "https://registry.npmjs.org/node-releases/-/node-releases-1.1.50.tgz#803c40d2c45db172d0410e4efec83aa8c6ad0592" - integrity sha512-lgAmPv9eYZ0bGwUYAKlr8MG6K4CvWliWqnkcT2P8mMAgVrH3lqfBPorFlxiG1pHQnqmavJZ9vbMXUTNyMLbrgQ== - dependencies: - semver "^6.3.0" - nodemon@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/nodemon/-/nodemon-2.0.2.tgz#9c7efeaaf9b8259295a97e5d4585ba8f0cbe50b0" @@ -13344,9 +13125,9 @@ nodemon@^2.0.2: update-notifier "^2.5.0" nopt@^4.0.1, nopt@~4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" - integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= + version "4.0.3" + resolved "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" + integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== dependencies: abbrev "1" osenv "^0.1.4" @@ -13484,10 +13265,10 @@ npm-pick-manifest@^3.0.0, npm-pick-manifest@^3.0.2: npm-package-arg "^6.0.0" semver "^5.4.1" -npm-profile@^4.0.2: - version "4.0.3" - resolved "https://registry.npmjs.org/npm-profile/-/npm-profile-4.0.3.tgz#791ef03af801e47eb767b1ca10c5a0f545da6632" - integrity sha512-NHASi4pvCzHMq3Wy4kYFTvCwGPrTbuuuF8M4TwSFFLzbebDa9dfjh26HMGDzRz+MmDvNDBWBZufx19qYi5HQOw== +npm-profile@^4.0.2, npm-profile@^4.0.4: + version "4.0.4" + resolved "https://registry.npmjs.org/npm-profile/-/npm-profile-4.0.4.tgz#28ee94390e936df6d084263ee2061336a6a1581b" + integrity sha512-Ta8xq8TLMpqssF0H60BXS1A90iMoM6GeKwsmravJ6wYjWwSzcYBTdyWa3DZCYqPutacBMEm7cxiOkiIeCUAHDQ== dependencies: aproba "^1.1.2 || 2" figgy-pudding "^3.4.1" @@ -13526,9 +13307,9 @@ npm-user-validate@~1.0.0: integrity sha1-jOyg9c6gTU6TUZ73LQVXp1Ei6VE= npm@^6.10.3: - version "6.14.1" - resolved "https://registry.npmjs.org/npm/-/npm-6.14.1.tgz#3b80f6f1aa11a9868860dcf897665f80ab38a204" - integrity sha512-2hi3UF7g5VL8VKm46Bx5GAW28DPb8BJZbj2uONMBMhY8XkJ56lSmHJNFcjTQr7KHZqWqiBT5BugaQEy+Y/4T2g== + version "6.14.3" + resolved "https://registry.npmjs.org/npm/-/npm-6.14.3.tgz#a122618543c6670765cf5e827cd996b5552f9b65" + integrity sha512-3tQYVEEdSGQGYoXhZvNqW8faqCidfMMaL387RdDo4Uu5kQy4IgvJ13NIsWVMQ6e3QWlbicNMSpFiyzYfMUuPDw== dependencies: JSONStream "^1.3.5" abbrev "~1.1.1" @@ -13557,10 +13338,10 @@ npm@^6.10.3: fs-vacuum "~1.2.10" fs-write-stream-atomic "~1.0.10" gentle-fs "^2.3.0" - glob "^7.1.4" + glob "^7.1.6" graceful-fs "^4.2.3" has-unicode "~2.0.1" - hosted-git-info "^2.8.7" + hosted-git-info "^2.8.8" iferr "^1.0.2" infer-owner "^1.0.4" inflight "~1.0.6" @@ -13588,9 +13369,9 @@ npm@^6.10.3: lru-cache "^5.1.1" meant "~1.0.1" mississippi "^3.0.0" - mkdirp "~0.5.1" + mkdirp "^0.5.3" move-concurrently "^1.0.1" - node-gyp "^5.0.7" + node-gyp "^5.1.0" nopt "~4.0.1" normalize-package-data "^2.5.0" npm-audit-report "^1.3.2" @@ -13600,7 +13381,7 @@ npm@^6.10.3: npm-package-arg "^6.1.1" npm-packlist "^1.4.8" npm-pick-manifest "^3.0.2" - npm-profile "^4.0.2" + npm-profile "^4.0.4" npm-registry-fetch "^4.0.3" npm-user-validate "~1.0.0" npmlog "~4.1.2" @@ -13622,7 +13403,7 @@ npm@^6.10.3: readdir-scoped-modules "^1.1.0" request "^2.88.0" retry "^0.12.0" - rimraf "^2.6.3" + rimraf "^2.7.1" safe-buffer "^5.1.2" semver "^5.7.1" sha "^3.0.0" @@ -14400,9 +14181,9 @@ performance-now@^2.1.0: integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.0.7, picomatch@^2.2.1: - version "2.2.1" - resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz#21bac888b6ed8601f831ce7816e335bc779f0a4a" - integrity sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA== + version "2.2.2" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== pify@^2.0.0, pify@^2.3.0: version "2.3.0" @@ -14481,7 +14262,7 @@ pkg-up@2.0.0: dependencies: find-up "^2.1.0" -pkg-up@3.1.0: +pkg-up@3.1.0, pkg-up@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== @@ -14507,12 +14288,12 @@ pnp-webpack-plugin@1.5.0: dependencies: ts-pnp "^1.1.2" -pnp-webpack-plugin@1.6.0: - version "1.6.0" - resolved "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.0.tgz#d5c068013a2fdc82224ca50ed179c8fba9036a8e" - integrity sha512-ZcMGn/xF/fCOq+9kWMP9vVVxjIkMCja72oy3lziR7UHy0hHFZ57iVpQ71OtveVbmzeCmphBg8pxNdk/hlK99aQ== +pnp-webpack-plugin@1.6.4: + version "1.6.4" + resolved "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz#c9711ac4dc48a685dabafc86f8b6dd9f8df84149" + integrity sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg== dependencies: - ts-pnp "^1.1.2" + ts-pnp "^1.1.6" polished@^3.3.1: version "3.5.0" @@ -14879,9 +14660,9 @@ postcss-modules-local-by-default@^3.0.2: postcss-value-parser "^4.0.0" postcss-modules-scope@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.1.1.tgz#33d4fc946602eb5e9355c4165d68a10727689dba" - integrity sha512-OXRUPecnHCg8b9xWvldG/jUpRIGPNRka0r4D4j0ESUU2/5IOnpsjfPPmDprM3Ih8CgZ8FXjWqaniK5v4rWt3oQ== + version "2.2.0" + resolved "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz#385cae013cc7743f5a7d7602d1073a89eaae62ee" + integrity sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ== dependencies: postcss "^7.0.6" postcss-selector-parser "^6.0.0" @@ -15275,7 +15056,7 @@ prismjs@~1.17.0: optionalDependencies: clipboard "^2.0.0" -private@^0.1.6, private@~0.1.5: +private@^0.1.8, private@~0.1.5: version "0.1.8" resolved "https://registry.npmjs.org/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== @@ -15336,16 +15117,16 @@ promise@^7.1.1: asap "~2.0.3" promise@^8.0.3: - version "8.0.3" - resolved "https://registry.npmjs.org/promise/-/promise-8.0.3.tgz#f592e099c6cddc000d538ee7283bb190452b0bf6" - integrity sha512-HeRDUL1RJiLhyA0/grn+PTShlBAcLuh/1BJGtrvjwbvRDCTLLMEz9rOGCV+R3vHY4MixIuoMEd9Yq/XvsTPcjw== + version "8.1.0" + resolved "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz#697c25c3dfe7435dd79fcd58c38a135888eaf05e" + integrity sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q== dependencies: asap "~2.0.6" prompts@^2.0.1: - version "2.3.1" - resolved "https://registry.npmjs.org/prompts/-/prompts-2.3.1.tgz#b63a9ce2809f106fa9ae1277c275b167af46ea05" - integrity sha512-qIP2lQyCwYbdzcqHIUi2HAxiWixhoM9OdLCWf8txXsapC/X9YdsCoeyRIXE/GP+Q0J37Q7+XN/MFqbUa7IzXNA== + version "2.3.2" + resolved "https://registry.npmjs.org/prompts/-/prompts-2.3.2.tgz#480572d89ecf39566d2bd3fe2c9fccb7c4c0b068" + integrity sha512-Q06uKs2CkNYVID0VqwfAl9mipo99zkBv/n2JtWY89Yxa3ZabWSrs0e2KTudKVa3peLUvYXMefDqIleLPVUBZMA== dependencies: kleur "^3.0.3" sisteransi "^1.0.4" @@ -15504,9 +15285,9 @@ query-string@^4.1.0: strict-uri-encode "^1.0.0" query-string@^6.8.2: - version "6.11.0" - resolved "https://registry.npmjs.org/query-string/-/query-string-6.11.0.tgz#dc27a05733d1be66f16d0f83dfa957270f45f66d" - integrity sha512-jS+me8X3OEGFTsF6kF+vUUMFG/d3WUCvD7bHhfZP5784nOq1pjj8yau/u86nfOncmcN6ZkSWKWkKAvv/MGxzLA== + version "6.11.1" + resolved "https://registry.npmjs.org/query-string/-/query-string-6.11.1.tgz#ab021f275d463ce1b61e88f0ce6988b3e8fe7c2c" + integrity sha512-1ZvJOUl8ifkkBxu2ByVM/8GijMIPx+cef7u3yroO3Ogm4DOdZcF5dcrWTIlSHe3Pg/mtlt6/eFjObDfJureZZA== dependencies: decode-uri-component "^0.2.0" split-on-first "^1.0.0" @@ -15629,14 +15410,14 @@ react-clientside-effect@^1.2.2: dependencies: "@babel/runtime" "^7.0.0" -react-dev-utils@^10.2.0: - version "10.2.0" - resolved "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-10.2.0.tgz#b11cc48aa2be2502fb3c27a50d1dfa95cfa9dfe0" - integrity sha512-MwrvQW2TFjLblhqpDNeqCXHBkz3G5vc7k4wntgutAJZX4ia3o07eGKo6uYGhUOeJ0hfOxcpJFNFk7+4XCc1S8g== +react-dev-utils@^10.2.0, react-dev-utils@^10.2.1: + version "10.2.1" + resolved "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-10.2.1.tgz#f6de325ae25fa4d546d09df4bb1befdc6dd19c19" + integrity sha512-XxTbgJnYZmxuPtY3y/UV0D8/65NKkmaia4rXzViknVnZeVlklSh8u6TnaEYPfAi/Gh1TP4mEOXHI6jQOPbeakQ== dependencies: "@babel/code-frame" "7.8.3" address "1.1.2" - browserslist "4.8.6" + browserslist "4.10.0" chalk "2.4.2" cross-spawn "7.0.1" detect-port-alt "1.1.6" @@ -15653,7 +15434,7 @@ react-dev-utils@^10.2.0: loader-utils "1.2.3" open "^7.0.2" pkg-up "3.1.0" - react-error-overlay "^6.0.6" + react-error-overlay "^6.0.7" recursive-readdir "2.2.2" shell-quote "1.7.2" strip-ansi "6.0.0" @@ -15704,17 +15485,7 @@ react-docgen@^5.0.0: node-dir "^0.1.10" strip-indent "^3.0.0" -react-dom@^16.12.0: - version "16.13.0" - resolved "https://registry.npmjs.org/react-dom/-/react-dom-16.13.0.tgz#cdde54b48eb9e8a0ca1b3dc9943d9bb409b81866" - integrity sha512-y09d2c4cG220DzdlFkPTnVvGTszVvNpC73v+AaLGLHbkpy3SSgvYq8x0rNwPJ/Rk/CicTNgk0hbHNw1gMEZAXg== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - prop-types "^15.6.2" - scheduler "^0.19.0" - -react-dom@^16.8.3: +react-dom@^16.12.0, react-dom@^16.8.3: version "16.13.1" resolved "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f" integrity sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag== @@ -15732,16 +15503,11 @@ react-draggable@^4.0.3: classnames "^2.2.5" prop-types "^15.6.0" -react-error-overlay@^6.0.3: +react-error-overlay@^6.0.3, react-error-overlay@^6.0.7: version "6.0.7" resolved "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.7.tgz#1dcfb459ab671d53f660a991513cb2f0a0553108" integrity sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA== -react-error-overlay@^6.0.6: - version "6.0.6" - resolved "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.6.tgz#ac4d9dc4c1b5c536c2c312bf66aa2b09bfa384e2" - integrity sha512-Yzpno3enVzSrSCnnljmr4b/2KUQSMZaPuqmS26t9k4nW7uwJk6STWmH9heNjPuvqUTO3jOSPkHoKgO4+Dw7uIw== - react-fast-compare@^2.0.2, react-fast-compare@^2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9" @@ -15797,9 +15563,9 @@ react-inspector@^4.0.0: prop-types "^15.6.1" react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.8.4: - version "16.13.0" - resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.0.tgz#0f37c3613c34fe6b37cd7f763a0d6293ab15c527" - integrity sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA== + version "16.13.1" + resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== react-lifecycles-compat@^3.0.2, react-lifecycles-compat@^3.0.4: version "3.0.4" @@ -15857,31 +15623,31 @@ react-router@5.1.2: tiny-warning "^1.0.0" react-scripts@^3.4.0: - version "3.4.0" - resolved "https://registry.npmjs.org/react-scripts/-/react-scripts-3.4.0.tgz#f413680f0b5b937c8879ba1ffdae9b8c5b364bf5" - integrity sha512-pBqaAroFoHnFAkuX+uSK9Th1uEh2GYdGY2IG1I9/7HmuEf+ls3lLCk1p2GFYRSrLMz6ieQR/SyN6TLIGK3hKRg== + version "3.4.1" + resolved "https://registry.npmjs.org/react-scripts/-/react-scripts-3.4.1.tgz#f551298b5c71985cc491b9acf3c8e8c0ae3ada0a" + integrity sha512-JpTdi/0Sfd31mZA6Ukx+lq5j1JoKItX7qqEK4OiACjVQletM1P38g49d9/D0yTxp9FrSF+xpJFStkGgKEIRjlQ== dependencies: - "@babel/core" "7.8.4" + "@babel/core" "7.9.0" "@svgr/webpack" "4.3.3" "@typescript-eslint/eslint-plugin" "^2.10.0" "@typescript-eslint/parser" "^2.10.0" - babel-eslint "10.0.3" + babel-eslint "10.1.0" babel-jest "^24.9.0" - babel-loader "8.0.6" + babel-loader "8.1.0" babel-plugin-named-asset-import "^0.3.6" - babel-preset-react-app "^9.1.1" + babel-preset-react-app "^9.1.2" camelcase "^5.3.1" case-sensitive-paths-webpack-plugin "2.3.0" css-loader "3.4.2" dotenv "8.2.0" dotenv-expand "5.1.0" eslint "^6.6.0" - eslint-config-react-app "^5.2.0" + eslint-config-react-app "^5.2.1" eslint-loader "3.0.3" eslint-plugin-flowtype "4.6.0" - eslint-plugin-import "2.20.0" + eslint-plugin-import "2.20.1" eslint-plugin-jsx-a11y "6.2.3" - eslint-plugin-react "7.18.0" + eslint-plugin-react "7.19.0" eslint-plugin-react-hooks "^1.6.1" file-loader "4.3.0" fs-extra "^8.1.0" @@ -15893,24 +15659,24 @@ react-scripts@^3.4.0: jest-watch-typeahead "0.4.2" mini-css-extract-plugin "0.9.0" optimize-css-assets-webpack-plugin "5.0.3" - pnp-webpack-plugin "1.6.0" + pnp-webpack-plugin "1.6.4" postcss-flexbugs-fixes "4.1.0" postcss-loader "3.0.0" postcss-normalize "8.0.1" postcss-preset-env "6.7.0" postcss-safe-parser "4.0.1" react-app-polyfill "^1.0.6" - react-dev-utils "^10.2.0" + react-dev-utils "^10.2.1" resolve "1.15.0" resolve-url-loader "3.1.1" sass-loader "8.0.2" semver "6.3.0" style-loader "0.23.1" - terser-webpack-plugin "2.3.4" - ts-pnp "1.1.5" + terser-webpack-plugin "2.3.5" + ts-pnp "1.1.6" url-loader "2.3.0" - webpack "4.41.5" - webpack-dev-server "3.10.2" + webpack "4.42.0" + webpack-dev-server "3.10.3" webpack-manifest-plugin "2.2.0" workbox-webpack-plugin "4.3.1" optionalDependencies: @@ -15963,12 +15729,12 @@ react-transition-group@^4.3.0: prop-types "^15.6.2" react-use@^13.24.0: - version "13.26.3" - resolved "https://registry.npmjs.org/react-use/-/react-use-13.26.3.tgz#d278bb0f8c995c3684a15de2026fbb51bd04df02" - integrity sha512-81I+p1F7LFxdkGM3qiLjF9NUi2mTupvaiN76IAWL5JMY7BHTGSk6PFdzByqJrxnTJ/RBrbIVttmqvYKHOz/9Tw== + version "13.27.0" + resolved "https://registry.npmjs.org/react-use/-/react-use-13.27.0.tgz#53a619dc9213e2cbe65d6262e8b0e76641ade4aa" + integrity sha512-2lyTyqJWyvnaP/woVtDcFS4B5pUYz0FQWI9pVHk/6TBWom2x3/ziJthkEn/LbCA9Twv39xSQU7Dn0zdIWfsNTQ== dependencies: "@types/js-cookie" "2.2.5" - "@xobotyi/scrollbar-width" "1.9.3" + "@xobotyi/scrollbar-width" "1.9.4" copy-to-clipboard "^3.2.0" fast-deep-equal "^3.1.1" fast-shallow-equal "^1.0.0" @@ -15981,16 +15747,7 @@ react-use@^13.24.0: ts-easing "^0.2.0" tslib "^1.10.0" -react@^16.12.0: - version "16.13.0" - resolved "https://registry.npmjs.org/react/-/react-16.13.0.tgz#d046eabcdf64e457bbeed1e792e235e1b9934cf7" - integrity sha512-TSavZz2iSLkq5/oiE7gnFzmURKZMltmi193rm5HEoUDAXpzT9Kzw6oNZnGoai/4+fUnm7FqS5dwgUL34TujcWQ== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - prop-types "^15.6.2" - -react@^16.8.3: +react@^16.12.0, react@^16.8.3: version "16.13.1" resolved "https://registry.npmjs.org/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e" integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w== @@ -16267,10 +16024,10 @@ refractor@^2.4.1: parse-entities "^1.1.2" prismjs "~1.17.0" -regenerate-unicode-properties@^8.1.0: - version "8.1.0" - resolved "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e" - integrity sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA== +regenerate-unicode-properties@^8.2.0: + version "8.2.0" + resolved "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" + integrity sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA== dependencies: regenerate "^1.4.0" @@ -16289,22 +16046,18 @@ regenerator-runtime@^0.11.0: resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== -regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.3: - version "0.13.3" - resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5" - integrity sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw== - -regenerator-runtime@^0.13.4: +regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.3, regenerator-runtime@^0.13.4: version "0.13.5" resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA== -regenerator-transform@^0.14.0: - version "0.14.1" - resolved "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.1.tgz#3b2fce4e1ab7732c08f665dfdb314749c7ddd2fb" - integrity sha512-flVuee02C3FKRISbxhXl9mGzdbWUVHubl1SMaknjxkFB1/iqpJhArQUvRxOOPEc/9tAiX0BaQ28FJH10E4isSQ== +regenerator-transform@^0.14.2: + version "0.14.4" + resolved "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.4.tgz#5266857896518d1616a78a0479337a30ea974cc7" + integrity sha512-EaJaKPBI9GvKpvUz2mz4fhx7WPgvwRLY9v3hlNHWmAuJHI13T4nwKnNvm5RWJzEdnI5g5UwtOww+S8IdoUC2bw== dependencies: - private "^0.1.6" + "@babel/runtime" "^7.8.4" + private "^0.1.8" regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" @@ -16337,17 +16090,17 @@ regexpp@^3.0.0: resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.0.0.tgz#dd63982ee3300e67b41c1956f850aa680d9d330e" integrity sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g== -regexpu-core@^4.6.0: - version "4.6.0" - resolved "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.6.0.tgz#2037c18b327cfce8a6fea2a4ec441f2432afb8b6" - integrity sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg== +regexpu-core@^4.7.0: + version "4.7.0" + resolved "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz#fcbf458c50431b0bb7b45d6967b8192d91f3d938" + integrity sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ== dependencies: regenerate "^1.4.0" - regenerate-unicode-properties "^8.1.0" - regjsgen "^0.5.0" - regjsparser "^0.6.0" + regenerate-unicode-properties "^8.2.0" + regjsgen "^0.5.1" + regjsparser "^0.6.4" unicode-match-property-ecmascript "^1.0.4" - unicode-match-property-value-ecmascript "^1.1.0" + unicode-match-property-value-ecmascript "^1.2.0" registry-auth-token@^3.0.1: version "3.4.0" @@ -16371,15 +16124,15 @@ registry-url@^3.0.3: dependencies: rc "^1.0.1" -regjsgen@^0.5.0: +regjsgen@^0.5.1: version "0.5.1" resolved "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz#48f0bf1a5ea205196929c0d9798b42d1ed98443c" integrity sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg== -regjsparser@^0.6.0: - version "0.6.3" - resolved "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.3.tgz#74192c5805d35e9f5ebe3c1fb5b40d40a8a38460" - integrity sha512-8uZvYbnfAtEm9Ab8NTb3hdLwL4g/LQzEYP7Xs27T96abJCCE2d6r3cPZPQEsLKy0vRSGVNG+/zVGtLr86HQduA== +regjsparser@^0.6.4: + version "0.6.4" + resolved "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz#a769f8684308401a66e9b529d2436ff4d0666272" + integrity sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw== dependencies: jsesc "~0.5.0" @@ -16579,7 +16332,7 @@ resolve@1.15.0: dependencies: path-parse "^1.0.6" -resolve@1.x, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.3.2, resolve@^1.8.1: +resolve@1.x, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.15.1, resolve@^1.3.2, resolve@^1.8.1: version "1.15.1" resolved "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8" integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w== @@ -16691,7 +16444,7 @@ rtl-css-js@^1.9.0: dependencies: "@babel/runtime" "^7.1.2" -run-async@^2.2.0: +run-async@^2.2.0, run-async@^2.4.0: version "2.4.0" resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz#e59054a5b86876cfae07f431d18cbaddc594f1e8" integrity sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg== @@ -16787,14 +16540,6 @@ saxes@^3.1.9: dependencies: xmlchars "^2.1.1" -scheduler@^0.19.0: - version "0.19.0" - resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.19.0.tgz#a715d56302de403df742f4a9be11975b32f5698d" - integrity sha512-xowbVaTPe9r7y7RUejcK73/j8tt2jfiyTednOvHbA8JoClvMYCp+r8QegLwK/n8zWQAtZb1fFnER4XLBZXrCxA== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - scheduler@^0.19.1: version "0.19.1" resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196" @@ -16812,7 +16557,7 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" -schema-utils@^2.0.1, schema-utils@^2.6.5: +schema-utils@^2.0.1, schema-utils@^2.5.0, schema-utils@^2.6.0, schema-utils@^2.6.1, schema-utils@^2.6.4, schema-utils@^2.6.5: version "2.6.5" resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.5.tgz#c758f0a7e624263073d396e29cd40aa101152d8a" integrity sha512-5KXuwKziQrTVHh8j/Uxz+QUbxkaLW9X/86NBlx/gnKgtsZA2GIVMUn17qWhRFwF8jdYb3Dig5hRO/W5mZqy6SQ== @@ -16820,14 +16565,6 @@ schema-utils@^2.0.1, schema-utils@^2.6.5: ajv "^6.12.0" ajv-keywords "^3.4.1" -schema-utils@^2.5.0, schema-utils@^2.6.0, schema-utils@^2.6.1, schema-utils@^2.6.4: - version "2.6.4" - resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.4.tgz#a27efbf6e4e78689d91872ee3ccfa57d7bdd0f53" - integrity sha512-VNjcaUxVnEeun6B2fiiUDjXXBtD4ZSH7pdbfIu1pOFwgptDPLMo/z9jr4sUfsjFVPqDCEin/F7IYlq7/E6yDbQ== - dependencies: - ajv "^6.10.2" - ajv-keywords "^3.4.1" - screenfull@^5.0.0: version "5.0.2" resolved "https://registry.npmjs.org/screenfull/-/screenfull-5.0.2.tgz#b9acdcf1ec676a948674df5cd0ff66b902b0bed7" @@ -17165,9 +16902,9 @@ simplebar@^4.2.3: resize-observer-polyfill "^1.5.1" sisteransi@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.4.tgz#386713f1ef688c7c0304dc4c0632898941cad2e3" - integrity sha512-/ekMoM4NJ59ivGSfKapeG+FWtrmWvA1p6FBZwXrqojw90vJu8lBmrTxCMuBCydKtkaUe2zt4PlxeTKpjwMbyig== + version "1.0.5" + resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== slash@^1.0.0: version "1.0.0" @@ -17901,9 +17638,9 @@ supports-hyperlinks@^2.0.0: supports-color "^7.0.0" svg-parser@^2.0.0: - version "2.0.3" - resolved "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.3.tgz#a38f2e4e5442986f7ecb554c11f1411cfcf8c2b9" - integrity sha512-fnCWiifNhK8i2Z7b9R5tbNahpxrRdAaQbnoxKlT2KrSCj9Kq/yBSgulCRgBJRhy1dPnSY5slg5ehPUnzpEcHlg== + version "2.0.4" + resolved "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" + integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ== svgo@^1.0.0, svgo@^1.2.2: version "1.3.2" @@ -18006,13 +17743,14 @@ temp-write@^3.4.0: temp-dir "^1.0.0" uuid "^3.0.1" -tempy@^0.4.0: - version "0.4.0" - resolved "https://registry.npmjs.org/tempy/-/tempy-0.4.0.tgz#2240e944cbb6b843415b7ef10b6dff9cda7297d0" - integrity sha512-mKnScm8aXv+cG6l1Nzp6mERGgC4UblbPnSDeQp83JgZ7xqDcnl+7u3+6zXnf1UE7YluDUTEIna1iKYwCSaOk9g== +tempy@^0.5.0: + version "0.5.0" + resolved "https://registry.npmjs.org/tempy/-/tempy-0.5.0.tgz#2785c89df39fcc4d1714fc554813225e1581d70b" + integrity sha512-VEY96x7gbIRfsxqsafy2l5yVxxp3PhwAGoWMyC2D2Zt5DmEv+2tGiPOrquNRpf21hhGnKLVEsuqleqiZmKG/qw== dependencies: + is-stream "^2.0.0" temp-dir "^2.0.0" - type-fest "^0.10.0" + type-fest "^0.12.0" unique-string "^2.0.0" term-size@^1.2.0: @@ -18035,10 +17773,10 @@ terminal-link@^2.0.0: ansi-escapes "^4.2.1" supports-hyperlinks "^2.0.0" -terser-webpack-plugin@2.3.4: - version "2.3.4" - resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.4.tgz#ac045703bd8da0936ce910d8fb6350d0e1dee5fe" - integrity sha512-Nv96Nws2R2nrFOpbzF6IxRDpIkkIfmhvOws+IqMvYdFLO7o6wAILWFKONFgaYy8+T4LVz77DQW0f7wOeDEAjrg== +terser-webpack-plugin@2.3.5, terser-webpack-plugin@^2.1.2: + version "2.3.5" + resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.5.tgz#5ad971acce5c517440ba873ea4f09687de2f4a81" + integrity sha512-WlWksUoq+E4+JlJ+h+U+QUzXpcsMSSNXkDy9lBVkSqDn1w23Gg29L/ary9GeJVYCGiNJJX7LnVc4bwL1N3/g1w== dependencies: cacache "^13.0.1" find-cache-dir "^3.2.0" @@ -18065,34 +17803,10 @@ terser-webpack-plugin@^1.4.3: webpack-sources "^1.4.0" worker-farm "^1.7.0" -terser-webpack-plugin@^2.1.2: - version "2.3.5" - resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.5.tgz#5ad971acce5c517440ba873ea4f09687de2f4a81" - integrity sha512-WlWksUoq+E4+JlJ+h+U+QUzXpcsMSSNXkDy9lBVkSqDn1w23Gg29L/ary9GeJVYCGiNJJX7LnVc4bwL1N3/g1w== - dependencies: - cacache "^13.0.1" - find-cache-dir "^3.2.0" - jest-worker "^25.1.0" - p-limit "^2.2.2" - schema-utils "^2.6.4" - serialize-javascript "^2.1.2" - source-map "^0.6.1" - terser "^4.4.3" - webpack-sources "^1.4.3" - -terser@^4.1.2: - version "4.6.4" - resolved "https://registry.npmjs.org/terser/-/terser-4.6.4.tgz#40a0b37afbe5b57e494536815efa68326840fc00" - integrity sha512-5fqgBPLgVHZ/fVvqRhhUp9YUiGXhFJ9ZkrZWD9vQtFBR4QIGTnbsb+/kKqSqfgp3WnBwGWAFnedGTtmX1YTn0w== - dependencies: - commander "^2.20.0" - source-map "~0.6.1" - source-map-support "~0.5.12" - -terser@^4.4.3, terser@^4.6.3: - version "4.6.6" - resolved "https://registry.npmjs.org/terser/-/terser-4.6.6.tgz#da2382e6cafbdf86205e82fb9a115bd664d54863" - integrity sha512-4lYPyeNmstjIIESr/ysHg2vUPRGf2tzF9z2yYwnowXVuVzLEamPN1Gfrz7f8I9uEPuHcbFlW4PLIAsJoxXyJ1g== +terser@^4.1.2, terser@^4.4.3, terser@^4.6.3: + version "4.6.7" + resolved "https://registry.npmjs.org/terser/-/terser-4.6.7.tgz#478d7f9394ec1907f0e488c5f6a6a9a2bad55e72" + integrity sha512-fmr7M1f7DBly5cX2+rFDvmGBAaaZyPrHYK4mMdHEDAdNTqXSZgSOfqsfGq2HqPGT/1V0foZZuCZFx8CHKgAk3g== dependencies: commander "^2.20.0" source-map "~0.6.1" @@ -18348,7 +18062,7 @@ ts-easing@^0.2.0: resolved "https://registry.npmjs.org/ts-easing/-/ts-easing-0.2.0.tgz#c8a8a35025105566588d87dbda05dd7fbfa5a4ec" integrity sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ== -ts-jest@^25.0.0: +ts-jest@^25.2.1: version "25.2.1" resolved "https://registry.npmjs.org/ts-jest/-/ts-jest-25.2.1.tgz#49bf05da26a8b7fbfbc36b4ae2fcdc2fef35c85d" integrity sha512-TnntkEEjuXq/Gxpw7xToarmHbAafgCaAzOpnajnFC6jI7oo1trMzAHA04eWpc3MhV6+yvhE8uUBAmN+teRJh0A== @@ -18376,9 +18090,9 @@ ts-loader@^6.2.1: semver "^6.0.0" ts-node@^8.6.2: - version "8.6.2" - resolved "https://registry.npmjs.org/ts-node/-/ts-node-8.6.2.tgz#7419a01391a818fbafa6f826a33c1a13e9464e35" - integrity sha512-4mZEbofxGqLL2RImpe3zMJukvEvcO1XP8bj8ozBPySdCUXEcU5cIRwR0aM3R+VoZq7iXc8N86NC0FspGRqP4gg== + version "8.8.1" + resolved "https://registry.npmjs.org/ts-node/-/ts-node-8.8.1.tgz#7c4d3e9ed33aa703b64b28d7f9d194768be5064d" + integrity sha512-10DE9ONho06QORKAaCBpPiFCdW+tZJuY/84tyypGtl6r+/C7Asq0dhqbRZURuUlLQtZxxDvT8eoj8cGW0ha6Bg== dependencies: arg "^4.1.0" diff "^4.0.1" @@ -18386,12 +18100,7 @@ ts-node@^8.6.2: source-map-support "^0.5.6" yn "3.1.1" -ts-pnp@1.1.5: - version "1.1.5" - resolved "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.1.5.tgz#840e0739c89fce5f3abd9037bb091dbff16d9dec" - integrity sha512-ti7OGMOUOzo66wLF3liskw6YQIaSsBgc4GOAlWRnIEj8htCxJUxskanMUoJOD6MDCRAXo36goXJZch+nOS0VMA== - -ts-pnp@^1.1.2: +ts-pnp@1.1.6, ts-pnp@^1.1.2, ts-pnp@^1.1.6: version "1.1.6" resolved "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.1.6.tgz#389a24396d425a0d3162e96d2b4638900fdc289a" integrity sha512-CrG5GqAAzMT7144Cl+UIFP7mz/iIhiy+xQ6GGcnjTezhALT02uPMRw7tgDSESgB5MsfKt55+GPWw4ir1kVtMIQ== @@ -18437,10 +18146,15 @@ type-detect@4.0.8: resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== -type-fest@^0.10.0: - version "0.10.0" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.10.0.tgz#7f06b2b9fbfc581068d1341ffabd0349ceafc642" - integrity sha512-EUV9jo4sffrwlg8s0zDhP0T2WD3pru5Xi0+HTE3zTUmBaZNhfkite9PdSJwdXLwPVW0jnAHT56pZHIOYckPEiw== +type-fest@^0.11.0: + version "0.11.0" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" + integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== + +type-fest@^0.12.0: + version "0.12.0" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.12.0.tgz#f57a27ab81c68d136a51fd71467eff94157fa1ee" + integrity sha512-53RyidyjvkGpnWPMF9bQgFtWp+Sl8O2Rp13VavmJgfAP9WWG6q6TkrKU8iyJdnwnfgHI6k2hTlgqH4aSdjoTbg== type-fest@^0.3.0: version "0.3.1" @@ -18493,9 +18207,9 @@ typedarray@^0.0.6: integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= typescript@^3.7.4, typescript@^3.7.5: - version "3.8.2" - resolved "https://registry.npmjs.org/typescript/-/typescript-3.8.2.tgz#91d6868aaead7da74f493c553aeff76c0c0b1d5a" - integrity sha512-EgOVgL/4xfVrCMbhYKUQTdF37SQn4Iw73H5BgCrF1Abdun7Kwy/QZsE/ssAy0y4LxBbvua3PIbFsbRczWWnDdQ== + version "3.8.3" + resolved "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061" + integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w== ua-parser-js@^0.7.18: version "0.7.21" @@ -18553,15 +18267,15 @@ unicode-match-property-ecmascript@^1.0.4: unicode-canonical-property-names-ecmascript "^1.0.4" unicode-property-aliases-ecmascript "^1.0.4" -unicode-match-property-value-ecmascript@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz#5b4b426e08d13a80365e0d657ac7a6c1ec46a277" - integrity sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g== +unicode-match-property-value-ecmascript@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531" + integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ== unicode-property-aliases-ecmascript@^1.0.4: - version "1.0.5" - resolved "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz#a9cc6cc7ce63a0a3023fc99e341b94431d405a57" - integrity sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw== + version "1.1.0" + resolved "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4" + integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg== union-value@^1.0.0: version "1.0.1" @@ -18630,6 +18344,11 @@ universalify@^0.1.0: resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +universalify@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz#b61a1da173e8435b2fe3c67d29b9adf8594bd16d" + integrity sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug== + unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -18869,11 +18588,11 @@ vm-browserify@^1.0.1: integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== w3c-hr-time@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045" - integrity sha1-gqwr/2PZUOqeMYmlimViX+3xkEU= + version "1.0.2" + resolved "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" + integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== dependencies: - browser-process-hrtime "^0.1.2" + browser-process-hrtime "^1.0.0" w3c-xmlserializer@^1.1.2: version "1.1.2" @@ -18884,7 +18603,7 @@ w3c-xmlserializer@^1.1.2: webidl-conversions "^4.0.2" xml-name-validator "^3.0.0" -wait-for-expect@^3.0.0: +wait-for-expect@^3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/wait-for-expect/-/wait-for-expect-3.0.2.tgz#d2f14b2f7b778c9b82144109c8fa89ceaadaa463" integrity sha512-cfS1+DZxuav1aBYbaO/kE06EOS8yRw7qOFoD3XtjTkYvCvh3zUvNST8DXK/nPaeqIzIv3P3kL3lRJn8iwOiSag== @@ -18942,46 +18661,7 @@ webpack-dev-middleware@^3.7.0, webpack-dev-middleware@^3.7.2: range-parser "^1.2.1" webpack-log "^2.0.0" -webpack-dev-server@3.10.2: - version "3.10.2" - resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.10.2.tgz#3403287d674c7407aab6d9b3f72259ecd0aa0874" - integrity sha512-pxZKPYb+n77UN8u9YxXT4IaIrGcNtijh/mi8TXbErHmczw0DtPnMTTjHj+eNjkqLOaAZM/qD7V59j/qJsEiaZA== - dependencies: - ansi-html "0.0.7" - bonjour "^3.5.0" - chokidar "^2.1.8" - compression "^1.7.4" - connect-history-api-fallback "^1.6.0" - debug "^4.1.1" - del "^4.1.1" - express "^4.17.1" - html-entities "^1.2.1" - http-proxy-middleware "0.19.1" - import-local "^2.0.0" - internal-ip "^4.3.0" - ip "^1.1.5" - is-absolute-url "^3.0.3" - killable "^1.0.1" - loglevel "^1.6.6" - opn "^5.5.0" - p-retry "^3.0.1" - portfinder "^1.0.25" - schema-utils "^1.0.0" - selfsigned "^1.10.7" - semver "^6.3.0" - serve-index "^1.9.1" - sockjs "0.3.19" - sockjs-client "1.4.0" - spdy "^4.0.1" - strip-ansi "^3.0.1" - supports-color "^6.1.0" - url "^0.11.0" - webpack-dev-middleware "^3.7.2" - webpack-log "^2.0.0" - ws "^6.2.1" - yargs "12.0.5" - -webpack-dev-server@^3.10.3: +webpack-dev-server@3.10.3, webpack-dev-server@^3.10.3: version "3.10.3" resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.10.3.tgz#f35945036813e57ef582c2420ef7b470e14d3af0" integrity sha512-e4nWev8YzEVNdOMcNzNeCN947sWJNd43E5XvsJzbAL08kGc2frm1tQ32hTJslRS+H65LCb/AaUCYU7fjHCpDeQ== @@ -19063,36 +18743,7 @@ webpack-virtual-modules@^0.2.0: dependencies: debug "^3.0.0" -webpack@4.41.5: - version "4.41.5" - resolved "https://registry.npmjs.org/webpack/-/webpack-4.41.5.tgz#3210f1886bce5310e62bb97204d18c263341b77c" - integrity sha512-wp0Co4vpyumnp3KlkmpM5LWuzvZYayDwM2n17EHFr4qxBBbRokC7DJawPJC7TfSFZ9HZ6GsdH40EBj4UV0nmpw== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-module-context" "1.8.5" - "@webassemblyjs/wasm-edit" "1.8.5" - "@webassemblyjs/wasm-parser" "1.8.5" - acorn "^6.2.1" - ajv "^6.10.2" - ajv-keywords "^3.4.1" - chrome-trace-event "^1.0.2" - enhanced-resolve "^4.1.0" - eslint-scope "^4.0.3" - json-parse-better-errors "^1.0.2" - loader-runner "^2.4.0" - loader-utils "^1.2.3" - memory-fs "^0.4.1" - micromatch "^3.1.10" - mkdirp "^0.5.1" - neo-async "^2.6.1" - node-libs-browser "^2.2.1" - schema-utils "^1.0.0" - tapable "^1.1.3" - terser-webpack-plugin "^1.4.3" - watchpack "^1.6.0" - webpack-sources "^1.4.1" - -webpack@^4.33.0, webpack@^4.38.0: +webpack@4.42.0, webpack@^4.33.0, webpack@^4.38.0, webpack@^4.41.6: version "4.42.0" resolved "https://registry.npmjs.org/webpack/-/webpack-4.42.0.tgz#b901635dd6179391d90740a63c93f76f39883eb8" integrity sha512-EzJRHvwQyBiYrYqhyjW9AqM90dE4+s1/XtCfn7uWg6cS72zH+2VPFAlsnW0+W0cDi0XRjNKUMoJtpSi50+Ph6w== @@ -19121,35 +18772,6 @@ webpack@^4.33.0, webpack@^4.38.0: watchpack "^1.6.0" webpack-sources "^1.4.1" -webpack@^4.41.6: - version "4.41.6" - resolved "https://registry.npmjs.org/webpack/-/webpack-4.41.6.tgz#12f2f804bf6542ef166755050d4afbc8f66ba7e1" - integrity sha512-yxXfV0Zv9WMGRD+QexkZzmGIh54bsvEs+9aRWxnN8erLWEOehAKUTeNBoUbA6HPEZPlRo7KDi2ZcNveoZgK9MA== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-module-context" "1.8.5" - "@webassemblyjs/wasm-edit" "1.8.5" - "@webassemblyjs/wasm-parser" "1.8.5" - acorn "^6.2.1" - ajv "^6.10.2" - ajv-keywords "^3.4.1" - chrome-trace-event "^1.0.2" - enhanced-resolve "^4.1.0" - eslint-scope "^4.0.3" - json-parse-better-errors "^1.0.2" - loader-runner "^2.4.0" - loader-utils "^1.2.3" - memory-fs "^0.4.1" - micromatch "^3.1.10" - mkdirp "^0.5.1" - neo-async "^2.6.1" - node-libs-browser "^2.2.1" - schema-utils "^1.0.0" - tapable "^1.1.3" - terser-webpack-plugin "^1.4.3" - watchpack "^1.6.0" - webpack-sources "^1.4.1" - websocket-driver@>=0.5.1: version "0.7.3" resolved "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz#a2d4e0d4f4f116f1e6297eba58b05d430100e9f9" @@ -19526,9 +19148,9 @@ ws@^6.1.2, ws@^6.2.1: async-limiter "~1.0.0" ws@^7.0.0: - version "7.2.1" - resolved "https://registry.npmjs.org/ws/-/ws-7.2.1.tgz#03ed52423cd744084b2cf42ed197c8b65a936b8e" - integrity sha512-sucePNSafamSKoOqoNfBd8V0StlkzJKL2ZAhGQinCfNQ+oacw+Pk7lcdAElecBF2VkLNZRiIb5Oi1Q5lVUVt2A== + version "7.2.3" + resolved "https://registry.npmjs.org/ws/-/ws-7.2.3.tgz#a5411e1fb04d5ed0efee76d26d5c46d830c39b46" + integrity sha512-HTDl9G9hbkNDk98naoR/cHDws7+EyYMOdL1BmjsZXRUjf7d+MficC4B7HLUPlSiho0vg+CWKrGIt/VJBd1xunQ== xdg-basedir@^3.0.0: version "3.0.0" @@ -19550,6 +19172,13 @@ xmlchars@^2.1.1: resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== +xregexp@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/xregexp/-/xregexp-4.3.0.tgz#7e92e73d9174a99a59743f67a4ce879a04b5ae50" + integrity sha512-7jXDIFXh5yJ/orPn4SXjuVrWWoi4Cr8jfV1eHv9CixKSbU+jY4mxfrBwAuDvupPNKpMUY+FeIqsVw/JLT9+B8g== + dependencies: + "@babel/runtime-corejs3" "^7.8.3" + xtend@^4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" @@ -19581,11 +19210,11 @@ yallist@^4.0.0: integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== yaml@^1.7.2: - version "1.7.2" - resolved "https://registry.npmjs.org/yaml/-/yaml-1.7.2.tgz#f26aabf738590ab61efaca502358e48dc9f348b2" - integrity sha512-qXROVp90sb83XtAoqE8bP9RwAkTTZbugRUTm5YeFCBfNRPEp2YzTeqWiz7m5OORHzEvrA/qcGS8hp/E+MMROYw== + version "1.8.3" + resolved "https://registry.npmjs.org/yaml/-/yaml-1.8.3.tgz#2f420fca58b68ce3a332d0ca64be1d191dd3f87a" + integrity sha512-X/v7VDnK+sxbQ2Imq4Jt2PRUsRsP7UcpSl3Llg6+NRRqWLIvxkMFYtH1FmvwNGYRKKPa+EPA4qDBlI9WVG1UKw== dependencies: - "@babel/runtime" "^7.6.3" + "@babel/runtime" "^7.8.7" yargs-parser@^10.0.0: version "10.1.0" @@ -19602,18 +19231,18 @@ yargs-parser@^11.1.1: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^13.1.1: - version "13.1.1" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0" - integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ== +yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== dependencies: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^15.0.0: - version "15.0.0" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.0.tgz#cdd7a97490ec836195f59f3f4dbe5ea9e8f75f08" - integrity sha512-xLTUnCMc4JhxrPEPUYD5IBR1mWCK/aT6+RJ/K29JY2y1vD+FhtgKK0AXRWvI262q3QSffAQuTouFIKUuHX89wQ== +yargs-parser@^15.0.1: + version "15.0.1" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz#54786af40b820dcb2fb8025b11b4d659d76323b3" + integrity sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw== dependencies: camelcase "^5.0.0" decamelize "^1.2.0" @@ -19626,6 +19255,14 @@ yargs-parser@^16.1.0: camelcase "^5.0.0" decamelize "^1.2.0" +yargs-parser@^18.1.1: + version "18.1.1" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.1.tgz#bf7407b915427fc760fcbbccc6c82b4f0ffcbd37" + integrity sha512-KRHEsOM16IX7XuLnMOqImcPNbLVXMNHYAoFc3BKR8Ortl5gzDbtXvvEoGx9imk5E+X1VeNKNlcHr8B8vi+7ipA== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + yargs-parser@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9" @@ -19677,9 +19314,9 @@ yargs@^11.0.0: yargs-parser "^9.0.2" yargs@^13.3.0: - version "13.3.0" - resolved "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83" - integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA== + version "13.3.2" + resolved "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== dependencies: cliui "^5.0.0" find-up "^3.0.0" @@ -19690,12 +19327,12 @@ yargs@^13.3.0: string-width "^3.0.0" which-module "^2.0.0" y18n "^4.0.0" - yargs-parser "^13.1.1" + yargs-parser "^13.1.2" yargs@^14.2.2: - version "14.2.2" - resolved "https://registry.npmjs.org/yargs/-/yargs-14.2.2.tgz#2769564379009ff8597cdd38fba09da9b493c4b5" - integrity sha512-/4ld+4VV5RnrynMhPZJ/ZpOCGSCeghMykZ3BhdFBDa9Wy/RH6uEGNWDJog+aUlq+9OM1CFTgtYRW5Is1Po9NOA== + version "14.2.3" + resolved "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz#1a1c3edced1afb2a2fea33604bc6d1d8d688a414" + integrity sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg== dependencies: cliui "^5.0.0" decamelize "^1.2.0" @@ -19707,12 +19344,12 @@ yargs@^14.2.2: string-width "^3.0.0" which-module "^2.0.0" y18n "^4.0.0" - yargs-parser "^15.0.0" + yargs-parser "^15.0.1" yargs@^15.0.0, yargs@^15.0.1, yargs@^15.0.2: - version "15.1.0" - resolved "https://registry.npmjs.org/yargs/-/yargs-15.1.0.tgz#e111381f5830e863a89550bd4b136bb6a5f37219" - integrity sha512-T39FNN1b6hCW4SOIk1XyTOWxtXdcen0t+XYrysQmChzSipvhBO8Bj0nK1ozAasdk24dNWuMZvr4k24nz+8HHLg== + version "15.3.1" + resolved "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b" + integrity sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA== dependencies: cliui "^6.0.0" decamelize "^1.2.0" @@ -19724,7 +19361,7 @@ yargs@^15.0.0, yargs@^15.0.1, yargs@^15.0.2: string-width "^4.2.0" which-module "^2.0.0" y18n "^4.0.0" - yargs-parser "^16.1.0" + yargs-parser "^18.1.1" yargs@^8.0.2: version "8.0.2" From a14584b485ab046c3c348bf7483dfeae422cb6b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 20 Mar 2020 13:19:24 +0100 Subject: [PATCH 006/146] [cli] Add CODEOWNERS support to create-plugin --- .../{ => create-plugin}/createPlugin.test.ts | 0 .../{ => create-plugin}/createPlugin.ts | 32 +++++++ .../create-plugin/lib/codeowners.test.ts | 49 +++++++++++ .../commands/create-plugin/lib/codeowners.ts | 88 +++++++++++++++++++ packages/cli/src/index.ts | 2 +- 5 files changed, 170 insertions(+), 1 deletion(-) rename packages/cli/src/commands/{ => create-plugin}/createPlugin.test.ts (100%) rename packages/cli/src/commands/{ => create-plugin}/createPlugin.ts (91%) create mode 100644 packages/cli/src/commands/create-plugin/lib/codeowners.test.ts create mode 100644 packages/cli/src/commands/create-plugin/lib/codeowners.ts diff --git a/packages/cli/src/commands/createPlugin.test.ts b/packages/cli/src/commands/create-plugin/createPlugin.test.ts similarity index 100% rename from packages/cli/src/commands/createPlugin.test.ts rename to packages/cli/src/commands/create-plugin/createPlugin.test.ts diff --git a/packages/cli/src/commands/createPlugin.ts b/packages/cli/src/commands/create-plugin/createPlugin.ts similarity index 91% rename from packages/cli/src/commands/createPlugin.ts rename to packages/cli/src/commands/create-plugin/createPlugin.ts index 22679b2dfa..bd76ef6d21 100644 --- a/packages/cli/src/commands/createPlugin.ts +++ b/packages/cli/src/commands/create-plugin/createPlugin.ts @@ -25,6 +25,7 @@ import { resolve as resolvePath } from 'path'; import { realpathSync, existsSync } from 'fs'; import os from 'os'; import ora from 'ora'; +import { parseOwnerIds, addCodeownersEntry } from './lib/codeowners'; const MARKER_SUCCESS = chalk.green(` ✔︎\n`); const MARKER_FAILURE = chalk.red(` ✘\n`); @@ -343,16 +344,39 @@ const createPlugin = async () => { return true; }, }, + { + type: 'input', + name: 'owner', + message: chalk.blue( + 'Enter the owner(s) of the plugin. If specified, this will be added to CODEOWNERS for the plugin path. [optional]', + ), + validate: (value: any) => { + if (!value) { + return true; + } + + const ownerIds = parseOwnerIds(value); + if (!ownerIds) { + return chalk.red( + 'The owner must be a space separated list of team names (e.g. @org/team-name), usernames (e.g. @username), or the email addresses of users (e.g. user@example.com).', + ); + } + + return true; + }, + }, ]; const answers: Answers = await inquirer.prompt(questions); const rootDir = realpathSync(process.cwd()); + const codeownersPath = path.join(rootDir, '.github', 'CODEOWNERS'); const appPackage = resolvePath(rootDir, 'packages', 'app'); const cliPackage = resolvePath(__dirname, '..', '..'); const templateFolder = resolvePath(cliPackage, 'templates', 'default-plugin'); const tempDir = path.join(os.tmpdir(), answers.id); const pluginDir = path.join(rootDir, 'plugins', answers.id); const version = require(resolvePath(cliPackage, 'package.json')).version; + const ownerIds = parseOwnerIds(answers.owner); console.log(); console.log(chalk.green('Creating the plugin...')); @@ -369,6 +393,14 @@ const createPlugin = async () => { addPluginToApp(rootDir, answers.id); } + if (ownerIds && ownerIds.length) { + addCodeownersEntry( + codeownersPath, + path.join('plugins', answers.id), + ownerIds, + ); + } + console.log(); console.log( chalk.green( diff --git a/packages/cli/src/commands/create-plugin/lib/codeowners.test.ts b/packages/cli/src/commands/create-plugin/lib/codeowners.test.ts new file mode 100644 index 0000000000..89386522f5 --- /dev/null +++ b/packages/cli/src/commands/create-plugin/lib/codeowners.test.ts @@ -0,0 +1,49 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { isValidSingleOwnerId, parseOwnerIds } from './codeowners'; + +describe('codeowners', () => { + it('isValidSingleOwnerId', () => { + [ + '@foo', + '@a-b', + '@org-a/team-a', + '@a/b', + 'adam_driver+spam@deathstar.com', + ].forEach(id => { + expect(isValidSingleOwnerId(id)).toBeTruthy(); + }); + + [ + '', + '@', + '@/team-a', + '@orsdsd/', + 'adam_driver@deathstar', + 'something', + ].forEach(id => { + expect(isValidSingleOwnerId(id)).toBeFalsy(); + }); + }); + + it('parseOwnerIds', () => { + expect(parseOwnerIds('')).toBeUndefined(); + expect(parseOwnerIds('@foo')).toEqual(['@foo']); + expect(parseOwnerIds(' @foo @bar/baz ')).toEqual(['@foo', '@bar/baz']); + expect(parseOwnerIds(' @foo @bar/ ')).toBeUndefined(); + }); +}); diff --git a/packages/cli/src/commands/create-plugin/lib/codeowners.ts b/packages/cli/src/commands/create-plugin/lib/codeowners.ts new file mode 100644 index 0000000000..96e9a2f76c --- /dev/null +++ b/packages/cli/src/commands/create-plugin/lib/codeowners.ts @@ -0,0 +1,88 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs-extra'; + +const TEAM_ID_RE = /^@[-\w]+\/[-\w]+$/; +const USER_ID_RE = /^@[-\w]+$/; +const EMAIL_RE = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i; + +export function isValidSingleOwnerId(id: string): boolean { + if (!id || typeof id !== 'string') { + return false; + } + + return TEAM_ID_RE.test(id) || USER_ID_RE.test(id) || EMAIL_RE.test(id); +} + +export function parseOwnerIds( + spaceSeparatedOwnerIds: string, +): string[] | undefined { + if (!spaceSeparatedOwnerIds || typeof spaceSeparatedOwnerIds !== 'string') { + return undefined; + } + + const ids = spaceSeparatedOwnerIds.split(' ').filter(Boolean); + if (!ids.every(isValidSingleOwnerId)) { + return undefined; + } + + return ids; +} + +export function addCodeownersEntry( + codeownersFilePath: string, + ownedPath: string, + ownerIds: string[], +): void { + const allLines = fs.readFileSync(codeownersFilePath, 'utf8').split('\n'); + + // Only keep comments from the top of the file + const commentLines = []; + for (const line of allLines) { + if (line[0] !== '#') { + break; + } + commentLines.push(line); + } + + const oldDeclarationEntries = allLines + .filter(line => line[0] !== '#') + .map(line => line.split(/\s+/).filter(Boolean)) + .filter(items => items.length >= 2); + + const newDeclarationEntries = oldDeclarationEntries + .filter(items => items[0] !== '*') + .concat([[ownedPath, ...ownerIds]]) + .sort((l1, l2) => l1[0].localeCompare(l2[0])) + .concat([['*', '@spotify/backstage-core']]); + + // Calculate longest path to be able to align entries nicely + const longestOwnedPath = newDeclarationEntries.reduce( + (length, entry) => Math.max(length, entry[0].length), + 0, + ); + + const newDeclarationLines = newDeclarationEntries.map(entry => { + const entryPath = entry[0] + ' '.repeat(longestOwnedPath - entry[0].length); + const entryOwners = entry.slice(1); + return [entryPath, ...entryOwners].join(' '); + }); + + const newLines = [...commentLines, '', ...newDeclarationLines, '']; + + fs.writeFileSync(codeownersFilePath, newLines.join('\n'), 'utf8'); +} diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 4797cceaf2..5f24190237 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -17,7 +17,7 @@ import program from 'commander'; import chalk from 'chalk'; import fs from 'fs'; -import createPluginCommand from './commands/createPlugin'; +import createPluginCommand from './commands/create-plugin/createPlugin'; import watch from './commands/watch-deps'; import lintCommand from './commands/lint'; import testCommand from './commands/testCommand'; From 5d3974e8696850f12589cc06a18440130c77bb80 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 22 Mar 2020 21:17:55 +0100 Subject: [PATCH 007/146] github/workflows: cache node_modules --- .github/workflows/frontend.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index 8b9f22a909..24f6e31ecf 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -24,6 +24,11 @@ jobs: key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} restore-keys: | ${{ runner.os }}-yarn- + - name: cache node_modules + uses: actions/cache@v1 + with: + path: node_modules + key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: From 231c991fe064e836a1459e3991799028e43a5165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Sun, 22 Mar 2020 20:59:53 +0100 Subject: [PATCH 008/146] Address review comments --- .../commands/create-plugin/createPlugin.ts | 27 ++++++--- .../commands/create-plugin/lib/codeowners.ts | 59 ++++++++++++++----- scripts/cli-e2e-test.js | 2 + 3 files changed, 64 insertions(+), 24 deletions(-) diff --git a/packages/cli/src/commands/create-plugin/createPlugin.ts b/packages/cli/src/commands/create-plugin/createPlugin.ts index bd76ef6d21..112377cbda 100644 --- a/packages/cli/src/commands/create-plugin/createPlugin.ts +++ b/packages/cli/src/commands/create-plugin/createPlugin.ts @@ -25,7 +25,11 @@ import { resolve as resolvePath } from 'path'; import { realpathSync, existsSync } from 'fs'; import os from 'os'; import ora from 'ora'; -import { parseOwnerIds, addCodeownersEntry } from './lib/codeowners'; +import { + parseOwnerIds, + addCodeownersEntry, + getCodeownersFilePath, +} from './lib/codeowners'; const MARKER_SUCCESS = chalk.green(` ✔︎\n`); const MARKER_FAILURE = chalk.red(` ✘\n`); @@ -328,6 +332,9 @@ export const movePlugin = ( }; const createPlugin = async () => { + const rootDir = realpathSync(process.cwd()); + const codeownersPath = await getCodeownersFilePath(rootDir); + const questions: Question[] = [ { type: 'input', @@ -344,7 +351,10 @@ const createPlugin = async () => { return true; }, }, - { + ]; + + if (codeownersPath) { + questions.push({ type: 'input', name: 'owner', message: chalk.blue( @@ -364,14 +374,13 @@ const createPlugin = async () => { return true; }, - }, - ]; + }); + } + const answers: Answers = await inquirer.prompt(questions); - const rootDir = realpathSync(process.cwd()); - const codeownersPath = path.join(rootDir, '.github', 'CODEOWNERS'); const appPackage = resolvePath(rootDir, 'packages', 'app'); - const cliPackage = resolvePath(__dirname, '..', '..'); + const cliPackage = resolvePath(__dirname, '..', '..', '..'); const templateFolder = resolvePath(cliPackage, 'templates', 'default-plugin'); const tempDir = path.join(os.tmpdir(), answers.id); const pluginDir = path.join(rootDir, 'plugins', answers.id); @@ -394,8 +403,8 @@ const createPlugin = async () => { } if (ownerIds && ownerIds.length) { - addCodeownersEntry( - codeownersPath, + await addCodeownersEntry( + codeownersPath!, path.join('plugins', answers.id), ownerIds, ); diff --git a/packages/cli/src/commands/create-plugin/lib/codeowners.ts b/packages/cli/src/commands/create-plugin/lib/codeowners.ts index 96e9a2f76c..1ffa640f04 100644 --- a/packages/cli/src/commands/create-plugin/lib/codeowners.ts +++ b/packages/cli/src/commands/create-plugin/lib/codeowners.ts @@ -15,10 +15,35 @@ */ import fs from 'fs-extra'; +import path from 'path'; const TEAM_ID_RE = /^@[-\w]+\/[-\w]+$/; const USER_ID_RE = /^@[-\w]+$/; -const EMAIL_RE = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i; +const EMAIL_RE = /^[^@]+@[-.\w]+\.[-\w]+$/i; + +type CodeownersEntry = { + ownedPath: string; + ownerIds: string[]; +}; + +export async function getCodeownersFilePath( + rootDir: string, +): Promise { + const paths = [ + path.join(rootDir, '.github', 'CODEOWNERS'), + path.join(rootDir, '.gitlab', 'CODEOWNERS'), + path.join(rootDir, 'docs', 'CODEOWNERS'), + path.join(rootDir, 'CODEOWNERS'), + ]; + + for (const p of paths) { + if (await fs.pathExists(p)) { + return p; + } + } + + return undefined; +} export function isValidSingleOwnerId(id: string): boolean { if (!id || typeof id !== 'string') { @@ -43,12 +68,12 @@ export function parseOwnerIds( return ids; } -export function addCodeownersEntry( +export async function addCodeownersEntry( codeownersFilePath: string, ownedPath: string, ownerIds: string[], -): void { - const allLines = fs.readFileSync(codeownersFilePath, 'utf8').split('\n'); +): Promise { + const allLines = (await fs.readFile(codeownersFilePath, 'utf8')).split('\n'); // Only keep comments from the top of the file const commentLines = []; @@ -59,30 +84,34 @@ export function addCodeownersEntry( commentLines.push(line); } - const oldDeclarationEntries = allLines + const oldDeclarationEntries: CodeownersEntry[] = allLines .filter(line => line[0] !== '#') .map(line => line.split(/\s+/).filter(Boolean)) - .filter(items => items.length >= 2); + .filter(tokens => tokens.length >= 2) + .map(tokens => ({ + ownedPath: tokens[0], + ownerIds: tokens.slice(1), + })); const newDeclarationEntries = oldDeclarationEntries - .filter(items => items[0] !== '*') - .concat([[ownedPath, ...ownerIds]]) - .sort((l1, l2) => l1[0].localeCompare(l2[0])) - .concat([['*', '@spotify/backstage-core']]); + .filter(entry => entry.ownedPath !== '*') + .concat([{ ownedPath, ownerIds }]) + .sort((l1, l2) => l1.ownedPath.localeCompare(l2.ownedPath)) + .concat([{ ownedPath: '*', ownerIds: ['@spotify/backstage-core'] }]); // Calculate longest path to be able to align entries nicely const longestOwnedPath = newDeclarationEntries.reduce( - (length, entry) => Math.max(length, entry[0].length), + (length, entry) => Math.max(length, entry.ownedPath.length), 0, ); const newDeclarationLines = newDeclarationEntries.map(entry => { - const entryPath = entry[0] + ' '.repeat(longestOwnedPath - entry[0].length); - const entryOwners = entry.slice(1); - return [entryPath, ...entryOwners].join(' '); + const entryPath = + entry.ownedPath + ' '.repeat(longestOwnedPath - entry.ownedPath.length); + return [entryPath, ...entry.ownerIds].join(' '); }); const newLines = [...commentLines, '', ...newDeclarationLines, '']; - fs.writeFileSync(codeownersFilePath, newLines.join('\n'), 'utf8'); + await fs.writeFile(codeownersFilePath, newLines.join('\n'), 'utf8'); } diff --git a/scripts/cli-e2e-test.js b/scripts/cli-e2e-test.js index 8d2535fdc3..10be71b1c4 100644 --- a/scripts/cli-e2e-test.js +++ b/scripts/cli-e2e-test.js @@ -37,6 +37,8 @@ async function main() { const createPlugin = spawnPiped(['yarn', 'create-plugin']); createPlugin.stdin.write('test-plugin\n'); + await new Promise(resolve => setTimeout(resolve, 2000)); + createPlugin.stdin.write('@someuser\n'); print('Waiting for plugin create script to be done'); await waitForExit(createPlugin); From 4f959b94ac738f1c8c0838ea41f0e5dcbcb8265f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 23 Mar 2020 01:31:17 +0100 Subject: [PATCH 009/146] github/workflows/cli: run on windows as well --- .github/workflows/cli.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index 31bf396f5f..b8465d96e5 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -11,7 +11,7 @@ jobs: strategy: matrix: - os: [ubuntu-latest] + os: [ubuntu-latest, windows-latest] node-version: [12.x] name: Node ${{ matrix.node-version }} on ${{ matrix.os }} @@ -33,12 +33,13 @@ jobs: - run: yarn install - run: yarn build # This creates a new plugin and pollutes the workspace, so it should be run last. - - name: verify app serve and plugin creation - shell: bash + - name: verify app serve and plugin creation on Windows + if: runner.os == 'Windows' + run: node scripts/cli-e2e-test.js + - name: verify app serve and plugin creation on Linux + if: runner.os == 'Linux' run: | - if [ "$RUNNER_OS" == "Linux" ]; then - sudo sysctl fs.inotify.max_user_watches=524288 - fi + sudo sysctl fs.inotify.max_user_watches=524288 node scripts/cli-e2e-test.js - name: yarn lint, test after plugin creation working-directory: plugins/test-plugin From 7b22d178384e210d6a2f5df97fee9391632170ad Mon Sep 17 00:00:00 2001 From: Majd Date: Sun, 22 Mar 2020 14:56:52 -0400 Subject: [PATCH 010/146] added login page with github authentication option --- packages/app/src/components/Root/Root.tsx | 2 + packages/core/src/api/app/AppBuilder.tsx | 5 + .../core/src/api/app/LoginPage/LoginPage.tsx | 177 ++++++++++++++++++ packages/core/src/api/app/LoginPage/index.ts | 17 ++ 4 files changed, 201 insertions(+) create mode 100644 packages/core/src/api/app/LoginPage/LoginPage.tsx create mode 100644 packages/core/src/api/app/LoginPage/index.ts diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index af7759d86f..8795b50e1d 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -18,6 +18,7 @@ import React, { FC, useContext } from 'react'; import PropTypes from 'prop-types'; import { Link, makeStyles, Typography } from '@material-ui/core'; import HomeIcon from '@material-ui/icons/Home'; +import AccountCircle from '@material-ui/icons/AccountCircle'; import { Sidebar, SidebarPage, @@ -77,6 +78,7 @@ const Root: FC<{}> = ({ children }) => ( + diff --git a/packages/core/src/api/app/AppBuilder.tsx b/packages/core/src/api/app/AppBuilder.tsx index e2f56c4cdb..393388cc38 100644 --- a/packages/core/src/api/app/AppBuilder.tsx +++ b/packages/core/src/api/app/AppBuilder.tsx @@ -25,6 +25,7 @@ import { SystemIconKey, defaultSystemIcons, } from '../../icons'; +import LoginPage from './LoginPage'; class AppImpl implements App { constructor(private readonly systemIcons: SystemIcons) {} @@ -86,6 +87,10 @@ export default class AppBuilder { } } + routes.push( + , + ); + return () => ( diff --git a/packages/core/src/api/app/LoginPage/LoginPage.tsx b/packages/core/src/api/app/LoginPage/LoginPage.tsx new file mode 100644 index 0000000000..4f8214b35c --- /dev/null +++ b/packages/core/src/api/app/LoginPage/LoginPage.tsx @@ -0,0 +1,177 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { FC, useState } from 'react'; +import { GitHub as GitHubIcon } from '@material-ui/icons'; +import Page from '../../../layout/Page'; +import Header from '../../../layout/Header'; +import Content from '../../../layout/Content/Content'; +import ContentHeader from '../../../layout/ContentHeader/ContentHeader'; +import { + Grid, + Typography, + Button, + TextField, + List, + ListItem, + Link +} from '@material-ui/core'; +import InfoCard from '../../../layout/InfoCard/InfoCard'; + +enum AuthType { + GitHub, +} + +const LoginPage: FC<{}> = () => { + const [githubUsername, setGithubUsername] = useState(String); + const [githubPersonalAuthToken, setGithubPersonalAuthToken] = useState(String); + const [loginDetails, setLoginDetails] = useState(Object); + + const saveGithubInfo = (info: {}) => { + localStorage.setItem('githubLoginDetails', JSON.stringify(info)); + setLoginDetails(info); + }; + + const deleteGithubInfo = () => { + localStorage.removeItem('githubLoginDetails'); + setLoginDetails(undefined); + }; + + const handleTokenRegistration = (event: any) => { + switch (event.target.name) { + case 'github-username-tf': + setGithubUsername(event.target.value); + break; + case 'github-auth-tf': + setGithubPersonalAuthToken(event.target.value); + break; + default: + break; + } + }; + + const fetchGitHubToken = (username: String, token: String) => { + fetch('https://api.github.com/user', { + headers: new Headers({ + Authorization: `Basic ${btoa(`${username}:${token}`)}`, + 'Content-Type': 'application/x-www-form-urlencoded', + }), + }) + .then((response) => { + if (response.status === 200) return response.json(); + throw Error(`${response.status} ${response.statusText}`); + }) + .then((data) => { + const info = { + username: username, + token: token, + name: data.name || data.login, + }; + saveGithubInfo(info); + }) + .catch(() => {}); + }; + + const validateUsernameAndToken = (username: String, token: String) => { + if (username === undefined || username === null || username === '') + return false; + + if (token === undefined || token === null || token === '') return false; + + return true; + }; + + const authenticate = (type: AuthType) => { + switch (type) { + case AuthType.GitHub: + { + const username = githubUsername; + const token = githubPersonalAuthToken; + if (validateUsernameAndToken(username, token)) + fetchGitHubToken(username, token); + } + break; + default: + break; + } + }; + + const LoginIndicator = () => { + const ls = localStorage.getItem('githubLoginDetails'); + if (ls !== null) { + const obj = ls || loginDetails ? JSON.parse(ls) : loginDetails; + return ( + + {`Welcome, ${obj.name}!`} +
+ Logout +
+ ); + } + return ( + + Welcome, guest! + + ); + }; + + return ( + +
+ +
+ + + + + + + GitHub + + + + + + + + + + + + + + + + +
+ ); +}; + +export default LoginPage; \ No newline at end of file diff --git a/packages/core/src/api/app/LoginPage/index.ts b/packages/core/src/api/app/LoginPage/index.ts new file mode 100644 index 0000000000..094029448c --- /dev/null +++ b/packages/core/src/api/app/LoginPage/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { default } from './LoginPage'; From e29f55088e2f1343663c41a920346ed4ef3d1da5 Mon Sep 17 00:00:00 2001 From: Muhammad Rivki Date: Mon, 23 Mar 2020 08:14:42 +0700 Subject: [PATCH 011/146] chore: add build storybook script at root package --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 66eea1d006..1370c7eec8 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "test": "cross-env CI=true lerna run test -- --coverage", "create-plugin": "backstage-cli create-plugin", "lint": "lerna run lint", - "storybook": "yarn workspace @spotify-backstage/core storybook" + "storybook": "yarn workspace @spotify-backstage/core storybook", + "build-storybook": "yarn workspace @spotify-backstage/core build-storybook" }, "workspaces": { "packages": [ From fd4b1c8e366e2a402444d0bae9c7599b76b280f6 Mon Sep 17 00:00:00 2001 From: Muhammad Rivki Date: Mon, 23 Mar 2020 08:16:00 +0700 Subject: [PATCH 012/146] fix: auto generated d.ts files when build storybook --- packages/core/.storybook/main.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/core/.storybook/main.js b/packages/core/.storybook/main.js index f94725055c..06345558b4 100644 --- a/packages/core/.storybook/main.js +++ b/packages/core/.storybook/main.js @@ -10,6 +10,9 @@ module.exports = { use: [ { loader: require.resolve('ts-loader'), + options: { + transpileOnly: true + } }, ], }); From e3843a90ced50b504e43bdc73c2ee2dca28e0d8f Mon Sep 17 00:00:00 2001 From: Vern Burton Date: Sun, 22 Mar 2020 21:28:58 -0500 Subject: [PATCH 013/146] Creating docker-compose.yml with nginx config via a new site. --- docker-compose.yml | 21 +++++++++++++++++++++ docker/backstage.conf | 9 +++++++++ 2 files changed, 30 insertions(+) create mode 100644 docker-compose.yml create mode 100644 docker/backstage.conf diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000..90c2e6d395 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,21 @@ +version: '3.7' +services: + app: + volumes: + - app_dir:/app/packages/app/build + build: . + + web: + ports: + - 8080:80 + volumes: + - ./docker/backstage.conf:/etc/nginx/conf.d/backstage.conf + - type: volume + source: app_dir + target: /usr/share/backstage/html + volume: + nocopy: true + image: nginx:mainline + +volumes: + app_dir: \ No newline at end of file diff --git a/docker/backstage.conf b/docker/backstage.conf new file mode 100644 index 0000000000..b309ab0b01 --- /dev/null +++ b/docker/backstage.conf @@ -0,0 +1,9 @@ +server { + listen 80 default_server; + root /usr/share/backstage/html; + server_name backstage.local; + index index.html index.htm; + location / { + try_files $uri /index.html; + } +} \ No newline at end of file From 8ace44135d524d58d5b5652f3c983efc7a1d112e Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 23 Mar 2020 09:35:39 +0100 Subject: [PATCH 014/146] packages/core: do not require prop types for js --- packages/core/.eslintrc.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/core/.eslintrc.js b/packages/core/.eslintrc.js index e8b6139c06..c916fb4b8f 100644 --- a/packages/core/.eslintrc.js +++ b/packages/core/.eslintrc.js @@ -9,6 +9,8 @@ module.exports = { }, ], rules: { + // TODO: add prop types to JS and remove + 'react/prop-types': 0, 'jest/expect-expect': 0, }, }; From ec02d83f40b3156c18894464c3502edc8fb0ee28 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 19 Mar 2020 14:31:58 +0100 Subject: [PATCH 015/146] core/apis/ApiRef: use domain dot notation --- packages/core/src/api/apis/ApiRef.test.ts | 20 ++++++++++++++++---- packages/core/src/api/apis/ApiRef.ts | 4 ++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/packages/core/src/api/apis/ApiRef.test.ts b/packages/core/src/api/apis/ApiRef.test.ts index 902ab1bfbe..b592e327b0 100644 --- a/packages/core/src/api/apis/ApiRef.test.ts +++ b/packages/core/src/api/apis/ApiRef.test.ts @@ -25,14 +25,26 @@ describe('ApiRef', () => { expect(() => ref.T).toThrow('tried to read ApiRef.T of apiRef{abc}'); }); - it('should require a ascii letters only in id', () => { - for (const id of ['a', 'abc', 'ABC', 'aBC', 'aBc']) { + it('should reject invalid ids', () => { + for (const id of ['a', 'abc', 'a.b.c', 'ab.c', 'abc.abc.abc3']) { expect(new ApiRef({ id, description: '123' }).id).toBe(id); } - for (const id of ['123', 'ab-c', 'ab_c', 'a2c', '', '_']) { + for (const id of [ + '123', + 'ab-c', + 'ab_c', + '.', + '2ac', + 'ab.3a', + '.abc', + 'abc.', + 'ab..s', + '', + '_', + ]) { expect(() => new ApiRef({ id, description: '123' }).id).toThrow( - `API id must only contain ascii letters, got '${id}'`, + `API id must only contain lowercase alphanums separated by dots, got '${id}'`, ); } }); diff --git a/packages/core/src/api/apis/ApiRef.ts b/packages/core/src/api/apis/ApiRef.ts index c28bb100b9..b3f4d48428 100644 --- a/packages/core/src/api/apis/ApiRef.ts +++ b/packages/core/src/api/apis/ApiRef.ts @@ -21,9 +21,9 @@ export type ApiRefConfig = { export default class ApiRef { constructor(private readonly config: ApiRefConfig) { - if (!config.id.match(/^[a-zA-Z]+$/)) { + if (!config.id.match(/^[a-z][a-z0-9]*(\.[a-z][a-z0-9]*)*$/)) { throw new Error( - `API id must only contain ascii letters, got '${config.id}'`, + `API id must only contain lowercase alphanums separated by dots, got '${config.id}'`, ); } } From 0372c641254f2e2cc589d92006b8051985157ff2 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 19 Mar 2020 13:44:17 +0100 Subject: [PATCH 016/146] core: added errors api definition in new definitions folder --- .../core/src/api/apis/definitions/errors.ts | 62 +++++++++++++++++++ .../core/src/api/apis/definitions/index.ts | 23 +++++++ packages/core/src/api/apis/index.ts | 1 + 3 files changed, 86 insertions(+) create mode 100644 packages/core/src/api/apis/definitions/errors.ts create mode 100644 packages/core/src/api/apis/definitions/index.ts diff --git a/packages/core/src/api/apis/definitions/errors.ts b/packages/core/src/api/apis/definitions/errors.ts new file mode 100644 index 0000000000..eb195898d2 --- /dev/null +++ b/packages/core/src/api/apis/definitions/errors.ts @@ -0,0 +1,62 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import ApiRef from '../ApiRef'; + +/** + * Mirrors the javascript Error class, for the purpose of + * providing documentation and optional fields. + */ +export type Error = { + name: string; + message: string; + stack?: string; +}; + +/** + * Provides additional information about an error that was posted to the application. + */ +export type ErrorContext = { + // If set to true, this error should not be displayed to the user. Defaults to false. + hidden?: boolean; +}; + +/** + * The error API is used to report errors to the app, and display them to the user. + * + * Plugins can use this API as a method of displaying errors to the user, but also + * to report errors for collection by error reporting services. + * + * If an error can be displayed inline, e.g. as feedback in a form, that should be + * preferred over relying on this API to display the error. The main use of this api + * for displaying errors should be for asynchronous errors, such as a failing background process. + * + * Even if an error is displayed inline, it should still be reported through this api + * if it would be useful to collect or log it for debugging purposes, but with + * the hidden flag set. For example, an error arising from form field validation + * should probably not be reported, while a failed REST call would be useful to report. + */ +export type ErrorApi = { + /** + * Post an error for handling by the application. + */ + post(error: Error, context?: ErrorContext); +}; + +export const errorApiRef = new ApiRef({ + id: 'core.error', + description: 'Used to report errors and forward them to the app', +}); diff --git a/packages/core/src/api/apis/definitions/index.ts b/packages/core/src/api/apis/definitions/index.ts new file mode 100644 index 0000000000..02d2b893a6 --- /dev/null +++ b/packages/core/src/api/apis/definitions/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This folder contains definitions for all core APIs. +// +// Plugins should rely on these APIs for functionality as much as possible. +// +// If you think some API definition is missing, please open an Issue or send a PR! + +export * from './errors'; diff --git a/packages/core/src/api/apis/index.ts b/packages/core/src/api/apis/index.ts index a49a622858..650948edc0 100644 --- a/packages/core/src/api/apis/index.ts +++ b/packages/core/src/api/apis/index.ts @@ -18,3 +18,4 @@ export { default as ApiProvider, useApi } from './ApiProvider'; export { default as ApiRegistry } from './ApiRegistry'; export { default as ApiTestRegistry } from './ApiTestRegistry'; export * from './types'; +export * from './definitions'; From 4e584c9c71d6f153ce854a6c2b43d01cc8815666 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 19 Mar 2020 15:12:50 +0100 Subject: [PATCH 017/146] core/apis/ApiRegistry: narrow down types of implementations --- packages/core/src/api/apis/ApiRegistry.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/api/apis/ApiRegistry.ts b/packages/core/src/api/apis/ApiRegistry.ts index b7af906b6c..4149365806 100644 --- a/packages/core/src/api/apis/ApiRegistry.ts +++ b/packages/core/src/api/apis/ApiRegistry.ts @@ -22,7 +22,7 @@ type ApiImpl = readonly [ApiRef, T]; class ApiRegistryBuilder { private apis: ApiImpl[] = []; - add(api: ApiRef, impl: T): T { + add(api: ApiRef, impl: I): I { this.apis.push([api, impl]); return impl; } From 9b7dcfffbe78f4ecc28f50301567004b973d1530 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 19 Mar 2020 15:24:10 +0100 Subject: [PATCH 018/146] core,app: add a method for registering APIs --- packages/app/src/App.tsx | 2 ++ packages/app/src/apis.ts | 21 +++++++++++++++++++++ packages/core/src/api/app/AppBuilder.tsx | 24 +++++++++++++++++------- 3 files changed, 40 insertions(+), 7 deletions(-) create mode 100644 packages/app/src/apis.ts diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index cb4c4470ba..9bb5eec4e3 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -20,6 +20,7 @@ import React, { FC } from 'react'; import { BrowserRouter as Router } from 'react-router-dom'; import Root from './components/Root'; import * as plugins from './plugins'; +import apis from './apis'; const useStyles = makeStyles(theme => ({ '@global': { @@ -40,6 +41,7 @@ const useStyles = makeStyles(theme => ({ })); const app = createApp(); +app.registerApis(apis); app.registerPlugin(...Object.values(plugins)); const AppComponent = app.build(); diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts new file mode 100644 index 0000000000..1c41e306a0 --- /dev/null +++ b/packages/app/src/apis.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ApiHolder, ApiRegistry } from '@spotify-backstage/core'; + +const builder = ApiRegistry.builder(); + +export default builder.build() as ApiHolder; diff --git a/packages/core/src/api/app/AppBuilder.tsx b/packages/core/src/api/app/AppBuilder.tsx index 393388cc38..ab6f60a293 100644 --- a/packages/core/src/api/app/AppBuilder.tsx +++ b/packages/core/src/api/app/AppBuilder.tsx @@ -25,6 +25,7 @@ import { SystemIconKey, defaultSystemIcons, } from '../../icons'; +import { ApiHolder, ApiProvider } from '../apis'; import LoginPage from './LoginPage'; class AppImpl implements App { @@ -36,9 +37,14 @@ class AppImpl implements App { } export default class AppBuilder { + private apis?: ApiHolder; private systemIcons = { ...defaultSystemIcons }; private readonly plugins = new Set(); + registerApis(apis: ApiHolder) { + this.apis = apis; + } + registerIcons(icons: Partial) { this.systemIcons = { ...this.systemIcons, ...icons }; } @@ -91,13 +97,17 @@ export default class AppBuilder { , ); - return () => ( - - - {routes} - 404 Not Found} /> - - + let rendered = ( + + {routes} + 404 Not Found} /> + ); + + if (this.apis) { + rendered = ; + } + + return () => ; } } From 5ad9dfbcd474b7b538b21677ff1fff07c3741178 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 19 Mar 2020 15:33:45 +0100 Subject: [PATCH 019/146] app: add initial errorApi implmentation --- packages/app/src/apis.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index 1c41e306a0..cf8a94d9fe 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -14,8 +14,19 @@ * limitations under the License. */ -import { ApiHolder, ApiRegistry } from '@spotify-backstage/core'; +import { ApiHolder, ApiRegistry, errorApiRef } from '@spotify-backstage/core'; const builder = ApiRegistry.builder(); +class ErrorDialogForwarder { + post(error: Error) { + // eslint-disable-next-line no-console + console.error(`Received error, ${error}`); + } +} + +export const errorDialogForwarder = new ErrorDialogForwarder(); + +builder.add(errorApiRef, errorDialogForwarder); + export default builder.build() as ApiHolder; From a9aede10fcf5251320479491c90f94d2b9fea05e Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 19 Mar 2020 15:33:23 +0100 Subject: [PATCH 020/146] plugins/welcome: add error button that shows an example of consuming the error api --- .../WelcomePage/ErrorButton.test.tsx | 41 +++++++++++++++++++ .../components/WelcomePage/ErrorButton.tsx | 35 ++++++++++++++++ .../WelcomePage/WelcomePage.test.tsx | 18 ++++++-- .../components/WelcomePage/WelcomePage.tsx | 10 +++++ 4 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 plugins/welcome/src/components/WelcomePage/ErrorButton.test.tsx create mode 100644 plugins/welcome/src/components/WelcomePage/ErrorButton.tsx diff --git a/plugins/welcome/src/components/WelcomePage/ErrorButton.test.tsx b/plugins/welcome/src/components/WelcomePage/ErrorButton.test.tsx new file mode 100644 index 0000000000..791937a3b5 --- /dev/null +++ b/plugins/welcome/src/components/WelcomePage/ErrorButton.test.tsx @@ -0,0 +1,41 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { render, fireEvent } from '@testing-library/react'; +import ErrorButton from './ErrorButton'; +import { ApiRegistry, errorApiRef, ApiProvider } from '@spotify-backstage/core'; + +describe('ErrorButton', () => { + it('should trigger an error', () => { + const errorApi = { post: jest.fn() }; + + const rendered = render( + + + , + ); + + const button = rendered.getByText('Trigger an error!'); + expect(button).toBeInTheDocument(); + + expect(errorApi.post).not.toHaveBeenCalled(); + fireEvent.click(button); + expect(errorApi.post).toHaveBeenCalledWith( + expect.objectContaining({ message: 'Oh no!' }), + ); + }); +}); diff --git a/plugins/welcome/src/components/WelcomePage/ErrorButton.tsx b/plugins/welcome/src/components/WelcomePage/ErrorButton.tsx new file mode 100644 index 0000000000..fa73115d18 --- /dev/null +++ b/plugins/welcome/src/components/WelcomePage/ErrorButton.tsx @@ -0,0 +1,35 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { FC } from 'react'; +import { Button } from '@material-ui/core'; +import { errorApiRef, useApi } from '@spotify-backstage/core'; + +const ErrorButton: FC<{}> = () => { + const errorApi = useApi(errorApiRef); + + const handleClick = () => { + errorApi.post(new Error('Oh no!')); + }; + + return ( + + ); +}; + +export default ErrorButton; diff --git a/plugins/welcome/src/components/WelcomePage/WelcomePage.test.tsx b/plugins/welcome/src/components/WelcomePage/WelcomePage.test.tsx index a5be434b59..9787fa2a9a 100644 --- a/plugins/welcome/src/components/WelcomePage/WelcomePage.test.tsx +++ b/plugins/welcome/src/components/WelcomePage/WelcomePage.test.tsx @@ -18,14 +18,24 @@ import React from 'react'; import { render } from '@testing-library/react'; import WelcomePage from './WelcomePage'; import { ThemeProvider } from '@material-ui/core'; -import { BackstageTheme } from '@spotify-backstage/core'; +import { + BackstageTheme, + ApiProvider, + ApiRegistry, + errorApiRef, +} from '@spotify-backstage/core'; describe('WelcomePage', () => { it('should render', () => { + // TODO: use common test app with mock implementations of all core APIs const rendered = render( - - - , + + + + + , ); expect(rendered.baseElement).toBeInTheDocument(); }); diff --git a/plugins/welcome/src/components/WelcomePage/WelcomePage.tsx b/plugins/welcome/src/components/WelcomePage/WelcomePage.tsx index c77e9ae6aa..755e19e0ff 100644 --- a/plugins/welcome/src/components/WelcomePage/WelcomePage.tsx +++ b/plugins/welcome/src/components/WelcomePage/WelcomePage.tsx @@ -34,6 +34,7 @@ import { ContentHeader, SupportButton, } from '@spotify-backstage/core'; +import ErrorButton from './ErrorButton'; const WelcomePage: FC<{}> = () => { const profile = { givenName: '' }; @@ -113,6 +114,15 @@ const WelcomePage: FC<{}> = () => { + + + + The button below is an example of how to consume APIs. + +
+ +
+
From bdaa6031d3f7f5c8ec0dd93252529b8dc3e3660b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 19 Mar 2020 18:16:47 +0100 Subject: [PATCH 021/146] app: more elaborate error display --- packages/app/package.json | 1 + packages/app/src/App.tsx | 4 +- packages/app/src/apis.ts | 10 +-- .../components/ErrorDisplay/ErrorDisplay.tsx | 84 +++++++++++++++++++ .../app/src/components/ErrorDisplay/index.ts | 17 ++++ 5 files changed, 107 insertions(+), 9 deletions(-) create mode 100644 packages/app/src/components/ErrorDisplay/ErrorDisplay.tsx create mode 100644 packages/app/src/components/ErrorDisplay/index.ts diff --git a/packages/app/package.json b/packages/app/package.json index 90ff7fa0b9..4f581599a5 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -5,6 +5,7 @@ "dependencies": { "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "4.0.0-alpha.45", "@spotify-backstage/cli": "^0.1.0", "@spotify-backstage/core": "^0.1.0", "@spotify-backstage/plugin-home-page": "^0.1.0", diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 9bb5eec4e3..91b588041c 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -19,8 +19,9 @@ import { BackstageTheme, createApp } from '@spotify-backstage/core'; import React, { FC } from 'react'; import { BrowserRouter as Router } from 'react-router-dom'; import Root from './components/Root'; +import ErrorDisplay from './components/ErrorDisplay'; import * as plugins from './plugins'; -import apis from './apis'; +import apis, { errorDialogForwarder } from './apis'; const useStyles = makeStyles(theme => ({ '@global': { @@ -50,6 +51,7 @@ const App: FC<{}> = () => { return ( + diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index cf8a94d9fe..6e2de63795 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -15,17 +15,11 @@ */ import { ApiHolder, ApiRegistry, errorApiRef } from '@spotify-backstage/core'; +import { ErrorDisplayForwarder } from './components/ErrorDisplay/ErrorDisplay'; const builder = ApiRegistry.builder(); -class ErrorDialogForwarder { - post(error: Error) { - // eslint-disable-next-line no-console - console.error(`Received error, ${error}`); - } -} - -export const errorDialogForwarder = new ErrorDialogForwarder(); +export const errorDialogForwarder = new ErrorDisplayForwarder(); builder.add(errorApiRef, errorDialogForwarder); diff --git a/packages/app/src/components/ErrorDisplay/ErrorDisplay.tsx b/packages/app/src/components/ErrorDisplay/ErrorDisplay.tsx new file mode 100644 index 0000000000..0a67191b41 --- /dev/null +++ b/packages/app/src/components/ErrorDisplay/ErrorDisplay.tsx @@ -0,0 +1,84 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { FC, useEffect, useState } from 'react'; +import PropTypes from 'prop-types'; +import { Snackbar } from '@material-ui/core'; +import { Alert } from '@material-ui/lab'; +import { ErrorApi, ErrorContext } from '@spotify-backstage/core'; + +type SubscriberFunc = (error: Error) => void; +type Unsubscribe = () => void; + +export class ErrorDisplayForwarder implements ErrorApi { + private readonly subscribers = new Set(); + + post(error: Error, context?: ErrorContext) { + if (context?.hidden) { + return; + } + + this.subscribers.forEach(subscriber => subscriber(error)); + } + + subscribe(func: SubscriberFunc): Unsubscribe { + this.subscribers.add(func); + + return () => { + this.subscribers.delete(func); + }; + } +} + +type Props = { + forwarder: ErrorDisplayForwarder; +}; + +const ErrorDisplay: FC = ({ forwarder }) => { + const [errors, setErrors] = useState>([]); + + useEffect(() => { + return forwarder.subscribe(error => setErrors(errs => errs.concat(error))); + }, [forwarder]); + + if (errors.length === 0) { + return null; + } + + const [firstError] = errors; + + const handleClose = () => { + setErrors(errs => errs.filter(err => err !== firstError)); + }; + + return ( + + + {firstError.toString()} + + + ); +}; + +ErrorDisplay.propTypes = { + forwarder: PropTypes.instanceOf(ErrorDisplayForwarder).isRequired, +}; + +export default ErrorDisplay; diff --git a/packages/app/src/components/ErrorDisplay/index.ts b/packages/app/src/components/ErrorDisplay/index.ts new file mode 100644 index 0000000000..05e414061b --- /dev/null +++ b/packages/app/src/components/ErrorDisplay/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { default } from './ErrorDisplay'; From 392fc2a47fa5a9628f61ca32ed81dd2e0f42fb5d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 19 Mar 2020 18:21:42 +0100 Subject: [PATCH 022/146] app/components/ErrorDisplay: added some TODOs --- packages/app/src/components/ErrorDisplay/ErrorDisplay.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/app/src/components/ErrorDisplay/ErrorDisplay.tsx b/packages/app/src/components/ErrorDisplay/ErrorDisplay.tsx index 0a67191b41..307158d88c 100644 --- a/packages/app/src/components/ErrorDisplay/ErrorDisplay.tsx +++ b/packages/app/src/components/ErrorDisplay/ErrorDisplay.tsx @@ -23,6 +23,8 @@ import { ErrorApi, ErrorContext } from '@spotify-backstage/core'; type SubscriberFunc = (error: Error) => void; type Unsubscribe = () => void; +// TODO: figure out where to put implementations of APIs, both inside apps +// but also in core/separate package. export class ErrorDisplayForwarder implements ErrorApi { private readonly subscribers = new Set(); @@ -47,6 +49,7 @@ type Props = { forwarder: ErrorDisplayForwarder; }; +// TODO: improve on this and promote to a shared component for use by all apps. const ErrorDisplay: FC = ({ forwarder }) => { const [errors, setErrors] = useState>([]); From 4f13f2c7788b25e17603219fa4d58a3cd0dabbc9 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 23 Mar 2020 09:40:19 +0100 Subject: [PATCH 023/146] core/apis/errors: rename to error and don't export Error type --- packages/core/src/api/apis/definitions/{errors.ts => error.ts} | 2 +- packages/core/src/api/apis/definitions/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename packages/core/src/api/apis/definitions/{errors.ts => error.ts} (98%) diff --git a/packages/core/src/api/apis/definitions/errors.ts b/packages/core/src/api/apis/definitions/error.ts similarity index 98% rename from packages/core/src/api/apis/definitions/errors.ts rename to packages/core/src/api/apis/definitions/error.ts index eb195898d2..01a6509715 100644 --- a/packages/core/src/api/apis/definitions/errors.ts +++ b/packages/core/src/api/apis/definitions/error.ts @@ -20,7 +20,7 @@ import ApiRef from '../ApiRef'; * Mirrors the javascript Error class, for the purpose of * providing documentation and optional fields. */ -export type Error = { +type Error = { name: string; message: string; stack?: string; diff --git a/packages/core/src/api/apis/definitions/index.ts b/packages/core/src/api/apis/definitions/index.ts index 02d2b893a6..bdf2a0b6a2 100644 --- a/packages/core/src/api/apis/definitions/index.ts +++ b/packages/core/src/api/apis/definitions/index.ts @@ -20,4 +20,4 @@ // // If you think some API definition is missing, please open an Issue or send a PR! -export * from './errors'; +export * from './error'; From 9d977ccb9c3e8cd4a7ccc55dc4b494a17e572828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 23 Mar 2020 08:47:12 +0100 Subject: [PATCH 024/146] Fix act() warnings on newly created plugin test run --- packages/cli/templates/default-plugin/package.json.hbs | 3 ++- .../ExampleComponent/ExampleComponent.test.tsx.hbs | 2 ++ .../ExampleFetchComponent.test.tsx.hbs | 6 ++++-- packages/cli/templates/default-plugin/src/setupTests.ts | 1 + scripts/cli-e2e-test.js | 3 ++- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/cli/templates/default-plugin/package.json.hbs b/packages/cli/templates/default-plugin/package.json.hbs index c6587f326e..20b78a268b 100644 --- a/packages/cli/templates/default-plugin/package.json.hbs +++ b/packages/cli/templates/default-plugin/package.json.hbs @@ -12,7 +12,8 @@ }, "devDependencies": { "@spotify-backstage/cli": "^{{version}}", - "@types/testing-library__jest-dom": "5.0.2" + "@types/testing-library__jest-dom": "5.0.2", + "jest-fetch-mock": "^3.0.3" }, "dependencies": { "@material-ui/lab": "4.0.0-alpha.45" diff --git a/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs b/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs index 69884c6a44..360055dc3d 100644 --- a/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs +++ b/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs @@ -16,12 +16,14 @@ import React from 'react'; import { render } from '@testing-library/react'; +import mockFetch from 'jest-fetch-mock'; import ExampleComponent from './ExampleComponent'; import { ThemeProvider } from '@material-ui/core'; import { BackstageTheme } from '@spotify-backstage/core'; describe('ExampleComponent', () => { it('should render', () => { + mockFetch.mockResponse(() => new Promise(() => {})); const rendered = render( diff --git a/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs b/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs index 7dff1e6ccc..7fecdc6f11 100644 --- a/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs +++ b/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs @@ -16,11 +16,13 @@ import React from 'react'; import { render } from '@testing-library/react'; +import mockFetch from 'jest-fetch-mock'; import ExampleFetchComponent from './ExampleFetchComponent'; describe('ExampleFetchComponent', () => { - it('should render', () => { + it('should render', async () => { + mockFetch.mockResponse(() => new Promise(() => {})); const rendered = render(); - expect(rendered.getByTestId('progress')).toBeInTheDocument(); + expect(await rendered.findByTestId('progress')).toBeInTheDocument(); }); }); diff --git a/packages/cli/templates/default-plugin/src/setupTests.ts b/packages/cli/templates/default-plugin/src/setupTests.ts index 8925258421..1a907ab8e6 100644 --- a/packages/cli/templates/default-plugin/src/setupTests.ts +++ b/packages/cli/templates/default-plugin/src/setupTests.ts @@ -15,3 +15,4 @@ */ import '@testing-library/jest-dom/extend-expect'; +require('jest-fetch-mock').enableMocks(); diff --git a/scripts/cli-e2e-test.js b/scripts/cli-e2e-test.js index 10be71b1c4..b495a37440 100644 --- a/scripts/cli-e2e-test.js +++ b/scripts/cli-e2e-test.js @@ -37,7 +37,8 @@ async function main() { const createPlugin = spawnPiped(['yarn', 'create-plugin']); createPlugin.stdin.write('test-plugin\n'); - await new Promise(resolve => setTimeout(resolve, 2000)); + // TODO: Add code to await the right prompts from create-plugin stdout instead + await new Promise(resolve => setTimeout(resolve, 5000)); createPlugin.stdin.write('@someuser\n'); print('Waiting for plugin create script to be done'); From 1d3a8c110cf30a8630e35c5973abca00c9b49180 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 23 Mar 2020 11:53:42 +0100 Subject: [PATCH 025/146] Better waiting --- scripts/cli-e2e-test.js | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/scripts/cli-e2e-test.js b/scripts/cli-e2e-test.js index b495a37440..3d4b9c04d1 100644 --- a/scripts/cli-e2e-test.js +++ b/scripts/cli-e2e-test.js @@ -36,9 +36,16 @@ async function main() { print('Backstage loaded correctly, creating plugin'); const createPlugin = spawnPiped(['yarn', 'create-plugin']); + + let stdout = ''; + createPlugin.stdout.on('data', data => { + stdout = stdout + data.toString('utf8'); + }); + + await waitFor(() => stdout.includes('Enter an ID for the plugin')); createPlugin.stdin.write('test-plugin\n'); - // TODO: Add code to await the right prompts from create-plugin stdout instead - await new Promise(resolve => setTimeout(resolve, 5000)); + + await waitFor(() => stdout.includes('Enter the owner(s) of the plugin')); createPlugin.stdin.write('@someuser\n'); print('Waiting for plugin create script to be done'); @@ -60,6 +67,18 @@ async function main() { process.exit(0); } +function waitFor(fn) { + return new Promise(resolve => { + const handle = setInterval(() => { + if (fn()) { + clearInterval(handle); + resolve(); + return; + } + }, 100); + }); +} + function print(msg) { return process.stdout.write(`${msg}\n`); } @@ -118,7 +137,7 @@ async function waitForPageWithText( browser, path, text, - { intervalMs = 1000, maxAttempts = 60 } = {}, + { intervalMs = 1000, maxAttempts = 120 } = {}, ) { let attempts = 0; for (;;) { From 4083680e8c61e924aa178e80859606322caeb52b Mon Sep 17 00:00:00 2001 From: Muhammad Rivki Date: Mon, 23 Mar 2020 20:35:10 +0700 Subject: [PATCH 026/146] ci: add new jobs for deploy storybook --- .github/workflows/frontend.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index 2842442bf9..3769d329a1 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -3,6 +3,8 @@ name: Frontend CI on: pull_request: types: [opened, reopened, edited, synchronize] + push: + branches: master jobs: build: @@ -40,3 +42,25 @@ jobs: yarn test env: CI: true + + jobs: + deoloy-storybook: + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [12.x] + steps: + - uses: actions/checkout@v2 + - name: build storybook + if: github.event_name === 'push' && github.ref == 'refs/heads/master' + run: | + yarn build-storybook + - name: deploy gh-pages + if: github.event_name === 'push' && github.ref == 'refs/heads/master' + uses: JamesIves/github-pages-deploy-action@3.4.2 + with: + ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} + BRANCH: gh-pages + FOLDER: packages/core/storybook-static From 2a3b7a0a53060ffafb26b5f5dcc7f507b81949ce Mon Sep 17 00:00:00 2001 From: Muhammad Rivki Date: Mon, 23 Mar 2020 20:41:19 +0700 Subject: [PATCH 027/146] fix: typo --- .github/workflows/frontend.yml | 39 +++++++++++++++++----------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index 3769d329a1..e6521b886c 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -43,24 +43,23 @@ jobs: env: CI: true - jobs: - deoloy-storybook: - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - runs-on: ubuntu-latest + deploy-storybook: + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + runs-on: ubuntu-latest - strategy: - matrix: - node-version: [12.x] - steps: - - uses: actions/checkout@v2 - - name: build storybook - if: github.event_name === 'push' && github.ref == 'refs/heads/master' - run: | - yarn build-storybook - - name: deploy gh-pages - if: github.event_name === 'push' && github.ref == 'refs/heads/master' - uses: JamesIves/github-pages-deploy-action@3.4.2 - with: - ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} - BRANCH: gh-pages - FOLDER: packages/core/storybook-static + strategy: + matrix: + node-version: [12.x] + steps: + - uses: actions/checkout@v2 + - name: build storybook + if: github.event_name === 'push' && github.ref == 'refs/heads/master' + run: | + yarn build-storybook + - name: deploy gh-pages + if: github.event_name === 'push' && github.ref == 'refs/heads/master' + uses: JamesIves/github-pages-deploy-action@3.4.2 + with: + ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} + BRANCH: gh-pages + FOLDER: packages/core/storybook-static From 184ad42160c927265f6f949d0fb1e9aa4bfb795a Mon Sep 17 00:00:00 2001 From: Muhammad Rivki Date: Mon, 23 Mar 2020 20:42:34 +0700 Subject: [PATCH 028/146] fix: equivalent --- .github/workflows/frontend.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index e6521b886c..8cc0dafafd 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -57,7 +57,7 @@ jobs: run: | yarn build-storybook - name: deploy gh-pages - if: github.event_name === 'push' && github.ref == 'refs/heads/master' + if: github.event_name == 'push' && github.ref == 'refs/heads/master' uses: JamesIves/github-pages-deploy-action@3.4.2 with: ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} From 9276d42e698b495f0b89d4f92fbdb8575d31e88b Mon Sep 17 00:00:00 2001 From: Muhammad Rivki Date: Mon, 23 Mar 2020 20:43:36 +0700 Subject: [PATCH 029/146] fix: remove unused conditional --- .github/workflows/frontend.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index 8cc0dafafd..0cd89f342d 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -52,12 +52,10 @@ jobs: node-version: [12.x] steps: - uses: actions/checkout@v2 - - name: build storybook - if: github.event_name === 'push' && github.ref == 'refs/heads/master' + - name: build storybook' run: | yarn build-storybook - name: deploy gh-pages - if: github.event_name == 'push' && github.ref == 'refs/heads/master' uses: JamesIves/github-pages-deploy-action@3.4.2 with: ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} From d154669e0caba095a696f04d039c50d808bd9374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 23 Mar 2020 14:44:23 +0100 Subject: [PATCH 030/146] Require node 12 --- .npmrc | 1 + package.json | 3 +++ 2 files changed, 4 insertions(+) diff --git a/.npmrc b/.npmrc index 214c29d139..c3c66347fd 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1,2 @@ registry=https://registry.npmjs.org/ +engine-strict=true diff --git a/package.json b/package.json index 1370c7eec8..75c1dd7474 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,9 @@ { "name": "root", "private": true, + "engines": { + "node": ">=12.0.0" + }, "scripts": { "start": "yarn build && yarn workspace @spotify-backstage/app start", "build": "lerna run build", From 4852485f9a16d02c19f5970561d8d567048c288b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 23 Mar 2020 16:58:16 +0100 Subject: [PATCH 031/146] package/core: move storybook to separate private and nohoist package --- packages/core/package.json | 13 ++----------- packages/storybook/.storybook/main.js | 22 ++++++++++++++++++++++ packages/storybook/package.json | 21 +++++++++++++++++++++ yarn.lock | 6 +++--- 4 files changed, 48 insertions(+), 14 deletions(-) create mode 100644 packages/storybook/.storybook/main.js create mode 100644 packages/storybook/package.json diff --git a/packages/core/package.json b/packages/core/package.json index 05ee131bc9..b6ceeb42f2 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -8,9 +8,7 @@ "scripts": { "build": "tsc --outDir dist/cjs --noEmit false --module CommonJS", "lint": "backstage-cli lint", - "test": "backstage-cli test", - "storybook": "start-storybook -p 6006", - "build-storybook": "build-storybook" + "test": "backstage-cli test" }, "dependencies": { "@material-ui/core": "^4.9.1", @@ -28,17 +26,10 @@ "recompose": "0.30.0" }, "devDependencies": { - "@babel/core": "^7.9.0", "@spotify-backstage/cli": "^0.1.0", - "@storybook/addon-actions": "^5.3.17", - "@storybook/addon-links": "^5.3.17", - "@storybook/addons": "^5.3.17", - "@storybook/react": "^5.3.17", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.3.2", "@testing-library/user-event": "^7.1.2", - "babel-loader": "^8.1.0", - "prop-types": "^15.7.2", - "ts-loader": "^6.2.1" + "prop-types": "^15.7.2" } } diff --git a/packages/storybook/.storybook/main.js b/packages/storybook/.storybook/main.js new file mode 100644 index 0000000000..7a51d41e0d --- /dev/null +++ b/packages/storybook/.storybook/main.js @@ -0,0 +1,22 @@ +module.exports = { + stories: [ + '../../core/src/layout/**/*.stories.tsx', + '../../core/src/components/**/*.stories.tsx', + ], + addons: ['@storybook/addon-actions', '@storybook/addon-links'], + webpackFinal: async config => { + config.module.rules.push({ + test: /\.(ts|tsx)$/, + use: [ + { + loader: require.resolve('ts-loader'), + options: { + transpileOnly: true, + }, + }, + ], + }); + config.resolve.extensions.push('.ts', '.tsx'); + return config; + }, +}; diff --git a/packages/storybook/package.json b/packages/storybook/package.json new file mode 100644 index 0000000000..f4e3b9d0ed --- /dev/null +++ b/packages/storybook/package.json @@ -0,0 +1,21 @@ +{ + "name": "storybook", + "version": "0.0.0", + "description": "Storybook build for core package", + "private": true, + "scripts": { + "start": "start-storybook -p 6006", + "build-storybook": "build-storybook --output-dir dist" + }, + "workspaces": { + "nohoist": [ + "@storybook/**" + ] + }, + "devDependencies": { + "@storybook/addon-actions": "^5.3.17", + "@storybook/addon-links": "^5.3.17", + "@storybook/addons": "^5.3.17", + "@storybook/react": "^5.3.17" + } +} diff --git a/yarn.lock b/yarn.lock index eaacf83d37..78f006747e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -25,7 +25,7 @@ invariant "^2.2.4" semver "^5.5.0" -"@babel/core@7.9.0", "@babel/core@^7.1.0", "@babel/core@^7.4.5", "@babel/core@^7.7.5", "@babel/core@^7.9.0": +"@babel/core@7.9.0", "@babel/core@^7.1.0", "@babel/core@^7.4.5", "@babel/core@^7.7.5": version "7.9.0" resolved "https://registry.npmjs.org/@babel/core/-/core-7.9.0.tgz#ac977b538b77e132ff706f3b8a4dbad09c03c56e" integrity sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w== @@ -4688,7 +4688,7 @@ babel-jest@^25.1.0: chalk "^3.0.0" slash "^3.0.0" -babel-loader@8.1.0, babel-loader@^8.1.0: +babel-loader@8.1.0: version "8.1.0" resolved "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz#c611d5112bd5209abe8b9fa84c3e4da25275f1c3" integrity sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw== @@ -12713,7 +12713,7 @@ minimist-options@^3.0.1: arrify "^1.0.1" is-plain-obj "^1.1.0" -minimist@1.2.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0: +minimist@1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= From f629c288ee81d9e0a195497d51598e7a3febe71d Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Tue, 24 Mar 2020 08:51:17 +0100 Subject: [PATCH 032/146] Add faq about repo managers --- docs/FAQ.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/FAQ.md b/docs/FAQ.md index 3e0093355e..0945ef696a 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -20,3 +20,12 @@ Additional open sourced plugins would be added to the `plugins` directory in thi While we encourage using the open soure model, integrators that want to experiment with Backstage internally may also choose to develop closed source plugins in a manner that suits them best, for example in their respective Backstage source repository. + +## Any plans for integrating with other repository managers such as Gitlab or Bitbucket? + +We chose Github by the fact that it is the tool that we are most familiar with and that will naturally +lead to integrations for Github specifically being developed in an early stage. + +Hosting this project on Github does not exclude integrations with other alternatives such as Gitlab or +Bitbucket. We believe that in time there will be plugins that will provide functionality for these tools +as well. Hopefully contributed by the community. From 6b8f0d3961d0c86447996726271ad91346f261dd Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Tue, 24 Mar 2020 09:06:42 +0100 Subject: [PATCH 033/146] Review comment --- docs/FAQ.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/FAQ.md b/docs/FAQ.md index 0945ef696a..9b9fff564e 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -29,3 +29,5 @@ lead to integrations for Github specifically being developed in an early stage. Hosting this project on Github does not exclude integrations with other alternatives such as Gitlab or Bitbucket. We believe that in time there will be plugins that will provide functionality for these tools as well. Hopefully contributed by the community. + +And note that implementations of Backstage can be hosted wherever you feel suits your needs best. From 250277a861dcd2d33977567c836384fdb676f8e4 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 24 Mar 2020 12:53:42 +0100 Subject: [PATCH 034/146] packages: mark core and cli as public --- packages/cli/package.json | 3 +++ packages/core/package.json | 3 +++ 2 files changed, 6 insertions(+) diff --git a/packages/cli/package.json b/packages/cli/package.json index 9cc9ee6aa7..95ea0f4867 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -11,6 +11,9 @@ "test": "backstage-cli test", "start": "nodemon ." }, + "publishConfig": { + "access": "public" + }, "devDependencies": { "@types/fs-extra": "^8.1.0", "@types/html-webpack-plugin": "^3.2.2", diff --git a/packages/core/package.json b/packages/core/package.json index 05ee131bc9..5a967bc84c 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -12,6 +12,9 @@ "storybook": "start-storybook -p 6006", "build-storybook": "build-storybook" }, + "publishConfig": { + "access": "public" + }, "dependencies": { "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", From f9a47d9a0af62aa29673ca14b1d9fd7e6119afaa Mon Sep 17 00:00:00 2001 From: Mateus Marquezini Date: Tue, 24 Mar 2020 09:38:06 -0300 Subject: [PATCH 035/146] Build and test only changed packages (#352) * #285 build and test only changed packages * changed since flag param * fix build cause --since lerna flag * fix build cause --since lerna flag * removed checkout option * fix build cause --since lerna flag * fix build cause --since lerna flag * fix build in progress * fix build in progress * removed --since flag for build executions * removed --since flag for build executions * changed action checkout step * added new ref to the action checkout * removed ref prop from the action checkout. Added new step to fetch branches to compare when using lerna --since * changed --since param value * added --since flag to the build and renamed the fetch step * removed --since flag from build. Added new config to the checkout step workflow * removed --since flag from yarn build. Added new config to the checkout step workflow * removed --since flag from yarn build. Added new step to checkout branch master * Added new step to checkout branch master --- .github/workflows/frontend.yml | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index 2842442bf9..527639abf1 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -14,6 +14,8 @@ jobs: steps: - uses: actions/checkout@v2 + - name: fetch branch master + run: git fetch origin master - name: find location of yarn cache id: yarn-cache run: echo "::set-output name=dir::$(yarn cache dir)" diff --git a/package.json b/package.json index 75c1dd7474..7c6f997efb 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "scripts": { "start": "yarn build && yarn workspace @spotify-backstage/app start", "build": "lerna run build", - "test": "cross-env CI=true lerna run test -- --coverage", + "test": "cross-env CI=true lerna run test --since origin/master -- --coverage", "create-plugin": "backstage-cli create-plugin", "lint": "lerna run lint", "storybook": "yarn workspace @spotify-backstage/core storybook", From 6686ca39c5cda4070ef0ea3fe0807eb8823ce522 Mon Sep 17 00:00:00 2001 From: Muhammad Rivki Date: Wed, 25 Mar 2020 01:43:47 +0700 Subject: [PATCH 036/146] ci: add filter trigger only when package/core change --- .github/workflows/frontend.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index 0cd89f342d..4b741a917f 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -4,7 +4,10 @@ on: pull_request: types: [opened, reopened, edited, synchronize] push: - branches: master + branches: + - master + paths: + - 'packages/core/src/**' jobs: build: @@ -54,10 +57,11 @@ jobs: - uses: actions/checkout@v2 - name: build storybook' run: | + yarn install yarn build-storybook - name: deploy gh-pages uses: JamesIves/github-pages-deploy-action@3.4.2 with: - ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} + ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} BRANCH: gh-pages FOLDER: packages/core/storybook-static From 8047b853ffa2e27f2dc55d6f00dba2236ef494e8 Mon Sep 17 00:00:00 2001 From: Muhammad Rivki Date: Wed, 25 Mar 2020 01:59:12 +0700 Subject: [PATCH 037/146] fix: update script and ci config --- .github/workflows/frontend.yml | 2 +- package.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index bc8e19ccec..e915b64fda 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -66,4 +66,4 @@ jobs: with: ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} BRANCH: gh-pages - FOLDER: packages/core/storybook-static + FOLDER: packages/storybook/dist diff --git a/package.json b/package.json index 7c6f997efb..b5126e606a 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,8 @@ "test": "cross-env CI=true lerna run test --since origin/master -- --coverage", "create-plugin": "backstage-cli create-plugin", "lint": "lerna run lint", - "storybook": "yarn workspace @spotify-backstage/core storybook", - "build-storybook": "yarn workspace @spotify-backstage/core build-storybook" + "storybook": "yarn workspace storybook start", + "build-storybook": "yarn workspace storybook build-storybook" }, "workspaces": { "packages": [ From 81ca63fd403d531f6b48067711721c9973b17f59 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 24 Mar 2020 22:04:48 +0100 Subject: [PATCH 038/146] workflows: add master build with publish --- .github/workflows/master.yml | 55 ++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 .github/workflows/master.yml diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml new file mode 100644 index 0000000000..408c2594e7 --- /dev/null +++ b/.github/workflows/master.yml @@ -0,0 +1,55 @@ +name: Master Build + +on: + push: + branches: [master] + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [12.x] + + steps: + - uses: actions/checkout@v2 + - name: find location of yarn cache + id: yarn-cache + run: echo "::set-output name=dir::$(yarn cache dir)" + - uses: actions/cache@v1 + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + - name: cache node_modules + uses: actions/cache@v1 + with: + path: node_modules + key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} + - name: use node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + registry-url: https://registry.npmjs.org/ # Needed for auth + - name: yarn install, build, and test + run: | + yarn install + yarn lint + yarn build + yarn test + env: + CI: true + # Publishes current version of packages that are not already present in the registry + - name: publish + run: npx --no-install lerna publish from-package --yes + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + CI: true + # Tags the commit with the version in the core package if the tag doesn't exist + - uses: Klemensas/action-autotag@1.2.3 + with: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + package_root: "packages/core" + tag_prefix: "v" From 9b8d15606e287a6cccf72163e72ee22a8b3f1653 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 24 Mar 2020 22:29:47 +0100 Subject: [PATCH 039/146] packages: make plugins private for now --- packages/cli/templates/default-plugin/package.json.hbs | 2 +- plugins/home-page/package.json | 1 + plugins/welcome/package.json | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/cli/templates/default-plugin/package.json.hbs b/packages/cli/templates/default-plugin/package.json.hbs index 20b78a268b..d8a303184c 100644 --- a/packages/cli/templates/default-plugin/package.json.hbs +++ b/packages/cli/templates/default-plugin/package.json.hbs @@ -4,7 +4,7 @@ "main": "dist/cjs/index.js", "types": "dist/cjs/index.d.ts", "license": "Apache-2.0", - "private": false, + "private": true, "scripts": { "build": "backstage-cli plugin:build", "lint": "backstage-cli lint", diff --git a/plugins/home-page/package.json b/plugins/home-page/package.json index 5380d8d25b..5869b5fde0 100644 --- a/plugins/home-page/package.json +++ b/plugins/home-page/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "main": "dist/cjs/index.js", "types": "dist/cjs/index.d.ts", + "private": true, "devDependencies": { "@spotify-backstage/cli": "^0.1.0", "@spotify-backstage/core": "^0.1.0", diff --git a/plugins/welcome/package.json b/plugins/welcome/package.json index 1d5f6a7373..734ba4fea6 100644 --- a/plugins/welcome/package.json +++ b/plugins/welcome/package.json @@ -4,7 +4,7 @@ "main": "dist/cjs/index.js", "types": "dist/cjs/index.d.ts", "license": "Apache-2.0", - "private": false, + "private": true, "scripts": { "build": "backstage-cli plugin:build", "lint": "backstage-cli lint", From ae61369fe18c2c78147836042751cb0e56f6259b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 24 Mar 2020 22:55:33 +0100 Subject: [PATCH 040/146] package: added root release command for creating a version bump commit --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 75c1dd7474..4b978bc5c9 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "build": "lerna run build", "test": "cross-env CI=true lerna run test -- --coverage", "create-plugin": "backstage-cli create-plugin", + "release": "if [ \"$(git symbolic-ref --short HEAD)\" = master ]; then echo \"don't try to release master\"; exit 1; else lerna version --no-push; fi", "lint": "lerna run lint", "storybook": "yarn workspace @spotify-backstage/core storybook", "build-storybook": "yarn workspace @spotify-backstage/core build-storybook" From 40c2d53365b16d3a638854494b6d09b51e1b5741 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 22 Mar 2020 16:08:45 +0100 Subject: [PATCH 041/146] cli: exclude templates dir from linting --- DEPLOYMENT.md | 38 +++++++++++++++++++ Dockerfile | 7 ---- deployment/Dockerfile.heroku | 24 ++++++++++++ .../default.conf.template | 2 +- 4 files changed, 63 insertions(+), 8 deletions(-) create mode 100644 DEPLOYMENT.md create mode 100644 deployment/Dockerfile.heroku rename default.conf.template => deployment/default.conf.template (98%) diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000000..47cd67e8ef --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,38 @@ +# Deploying Backstage + +## Heroku + +Deploying to heroku is relatively easy following these steps. + +First, make sure you have the [heroku CLI installed](https://devcenter.heroku.com/articles/heroku-cli) and log into it as well as loging into Heroku's [container registry](https://devcenter.heroku.com/articles/container-registry-and-runtime). + +```bash +$ heroku login +$ heroku container:login +``` + +You _might_ also need to set your Heroku app's stack to `container` + +```bash +$ heroku stack:set container -a +``` + +There are two small tweaks made to Backstage's default Dockerfile to run on Heroku: + +1. Do not run nginx as the `nginx` user. +2. Replace the standard port (80) in `/etc/nginx/conf.d/default.conf` with the wildcard `$PORT`, which Heroku will set for us. + +These changes are in the `deployment/Dockerfile.heroku` file, which you will need to copy into your root directory before deploying: + +```bash +$ cp deployment/Dockerfile.heroku Dockerfile +``` + +With these changes, we can build/push the Docker image to Heroku's container registry and release it to the `web` worker. + +```bash +$ heroku container:push web -a +$ heroku container:release web -a +``` + +With that, you should have Backstage up and running! diff --git a/Dockerfile b/Dockerfile index 3220e46f5f..39d51a35f2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,10 +15,3 @@ RUN yarn build FROM nginx:mainline COPY --from=builder /app/packages/app/build /usr/share/nginx/html - -# Run nginx as root -RUN sed -i 's/user nginx.*$//' /etc/nginx/nginx.conf - -# Copy in the nginx conf template and replace "$PORT" with the environment variable set by heroku -COPY default.conf.template /etc/nginx/conf.d/default.conf.template -CMD /bin/bash -c "envsubst '\$PORT' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf" && nginx -g 'daemon off;' diff --git a/deployment/Dockerfile.heroku b/deployment/Dockerfile.heroku new file mode 100644 index 0000000000..15878c67da --- /dev/null +++ b/deployment/Dockerfile.heroku @@ -0,0 +1,24 @@ +FROM node:12 as builder +WORKDIR /app + +COPY package.json yarn.lock .yarnrc .npmrc /app/ +COPY .yarn /app/.yarn +COPY packages /app/packages +COPY plugins /app/plugins + +RUN yarn + +COPY . . + +RUN yarn build + +FROM nginx:mainline + +COPY --from=builder /app/packages/app/build /usr/share/nginx/html + +# Run nginx as root +RUN sed -i 's/user nginx.*$//' /etc/nginx/nginx.conf + +# Copy in the nginx conf template and replace "$PORT" with the environment variable set by heroku +COPY deployment/default.conf.template /etc/nginx/conf.d/default.conf.template +CMD /bin/bash -c "envsubst '\$PORT' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf" && nginx -g 'daemon off;' diff --git a/default.conf.template b/deployment/default.conf.template similarity index 98% rename from default.conf.template rename to deployment/default.conf.template index 9d9ff7f3e2..61c32aa95e 100644 --- a/default.conf.template +++ b/deployment/default.conf.template @@ -5,4 +5,4 @@ server { root /usr/share/nginx/html; index index.html; } -} \ No newline at end of file +} From 123e5f80a8618e6df1ae0a5f99482761eb05df40 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 25 Mar 2020 01:24:49 +0100 Subject: [PATCH 042/146] package: update storybook command and remove build command --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 7c6f997efb..8a008a5ed3 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,7 @@ "test": "cross-env CI=true lerna run test --since origin/master -- --coverage", "create-plugin": "backstage-cli create-plugin", "lint": "lerna run lint", - "storybook": "yarn workspace @spotify-backstage/core storybook", - "build-storybook": "yarn workspace @spotify-backstage/core build-storybook" + "storybook": "yarn workspace storybook start" }, "workspaces": { "packages": [ From 2496a326c0f790bfff12b6ee6bc611ef39d7e2f4 Mon Sep 17 00:00:00 2001 From: Muhammad Rivki Date: Wed, 25 Mar 2020 13:17:32 +0700 Subject: [PATCH 043/146] ci: update token and build command --- .github/workflows/frontend.yml | 4 ++-- package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index e915b64fda..852d2e28e2 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -60,10 +60,10 @@ jobs: - name: build storybook' run: | yarn install - yarn build-storybook + yarn workspace storybook build-storybook - name: deploy gh-pages uses: JamesIves/github-pages-deploy-action@3.4.2 with: - ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} BRANCH: gh-pages FOLDER: packages/storybook/dist diff --git a/package.json b/package.json index b5126e606a..7c6f997efb 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,8 @@ "test": "cross-env CI=true lerna run test --since origin/master -- --coverage", "create-plugin": "backstage-cli create-plugin", "lint": "lerna run lint", - "storybook": "yarn workspace storybook start", - "build-storybook": "yarn workspace storybook build-storybook" + "storybook": "yarn workspace @spotify-backstage/core storybook", + "build-storybook": "yarn workspace @spotify-backstage/core build-storybook" }, "workspaces": { "packages": [ From 5012ce53353e1ff43ad8260b1590f07b3835c095 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 25 Mar 2020 11:09:53 +0100 Subject: [PATCH 044/146] github/workflows: update yarn cache step names --- .github/workflows/cli.yml | 5 +++-- .github/workflows/frontend.yml | 5 +++-- .github/workflows/master.yml | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index b8465d96e5..e7d8c53b90 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -17,10 +17,11 @@ jobs: name: Node ${{ matrix.node-version }} on ${{ matrix.os }} steps: - uses: actions/checkout@v2 - - name: find location of yarn cache + - name: find location of global yarn cache id: yarn-cache run: echo "::set-output name=dir::$(yarn cache dir)" - - uses: actions/cache@v1 + - name: cache global yarn cache + uses: actions/cache@v1 with: path: ${{ steps.yarn-cache.outputs.dir }} key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index 2842442bf9..be2a915b70 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -14,10 +14,11 @@ jobs: steps: - uses: actions/checkout@v2 - - name: find location of yarn cache + - name: find location of global yarn cache id: yarn-cache run: echo "::set-output name=dir::$(yarn cache dir)" - - uses: actions/cache@v1 + - name: cache global yarn cache + uses: actions/cache@v1 with: path: ${{ steps.yarn-cache.outputs.dir }} key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 408c2594e7..8e82c36373 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -14,10 +14,11 @@ jobs: steps: - uses: actions/checkout@v2 - - name: find location of yarn cache + - name: find location of global yarn cache id: yarn-cache run: echo "::set-output name=dir::$(yarn cache dir)" - - uses: actions/cache@v1 + - name: cache global yarn cache + uses: actions/cache@v1 with: path: ${{ steps.yarn-cache.outputs.dir }} key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} From 3dd16111806f53a1d201230b1b1c4d63de3379c9 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 25 Mar 2020 11:16:18 +0100 Subject: [PATCH 045/146] workflows/master: split yarn tasks into separate steps --- .github/workflows/master.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 8e82c36373..1985215a0e 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -12,6 +12,9 @@ jobs: matrix: node-version: [12.x] + env: + CI: true + steps: - uses: actions/checkout@v2 - name: find location of global yarn cache @@ -34,20 +37,15 @@ jobs: with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - - name: yarn install, build, and test - run: | - yarn install - yarn lint - yarn build - yarn test - env: - CI: true + - run: yarn install + - run: yarn lint + - run: yarn build + - run: yarn test # Publishes current version of packages that are not already present in the registry - name: publish run: npx --no-install lerna publish from-package --yes env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - CI: true # Tags the commit with the version in the core package if the tag doesn't exist - uses: Klemensas/action-autotag@1.2.3 with: From 59c1cba6b971822479d43086c2caa618fd70fa58 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 25 Mar 2020 12:11:17 +0100 Subject: [PATCH 046/146] docs: added npm publishing docs --- docs/README.md | 1 + docs/publishing.md | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 docs/publishing.md diff --git a/docs/README.md b/docs/README.md index 74e71370e1..c645b33024 100644 --- a/docs/README.md +++ b/docs/README.md @@ -5,3 +5,4 @@ Check out [https://backstage.io]() or see the table of content below. - [Architecture and terminology](architecture-terminology.md) - [Getting started](getting-started/README.md) - [References](reference/README.md) +- [Publishing](publishing.md) diff --git a/docs/publishing.md b/docs/publishing.md new file mode 100644 index 0000000000..15eaadd95d --- /dev/null +++ b/docs/publishing.md @@ -0,0 +1,37 @@ +# Publishing + +## NPM + +NPM packages are published through CI/CD in the +[.github/workflows/master.yml](../.github/workflows/master.yml) workflow. Every +commit that is merged to master will be checked for new versions of all public +packages, and any new versions will automatically be published to NPM. + +### Creating a new release + +Version bumps are made through release PRs. To create a new release, checkout out +a new branch that you will use for the release, e.g. + +```sh +$ git checkout -b new-release +``` + +Then, from the root of the repo, run + +```sh +$ yarn release +``` + +This will bring up the lerna release CLI where you choose what type of version bump +you want to make, (major/minor/patch/prerelease). The CLI will take you through choosing +a version, previewing all changes, and then approving the release. Once the release +is approved, a new commit is created that you can submit as a PR. Push the branch to GitHub: + +```sh +$ git push origin -u new-release +``` + +And then create a PR. Once the PR is approved and merged into master, the master build +will publish new versions of all bumped packages. + +[Back to Docs](README.md) From 80eeaa836bbaeaa97225a0304a736affbb0d5891 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 25 Mar 2020 12:40:14 +0100 Subject: [PATCH 047/146] workflows/frontend: split yarn tasks into separate steps (#383) --- .github/workflows/frontend.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index 527639abf1..fecab189e1 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -12,6 +12,9 @@ jobs: matrix: node-version: [12.x] + env: + CI: true + steps: - uses: actions/checkout@v2 - name: fetch branch master @@ -34,11 +37,7 @@ jobs: uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - - name: yarn install, build, and test - run: | - yarn install - yarn lint - yarn build - yarn test - env: - CI: true + - run: yarn install + - run: yarn lint + - run: yarn build + - run: yarn test From cf446052bc9f48f244700aac4b1a489fa7b8d42d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 25 Mar 2020 12:57:54 +0100 Subject: [PATCH 048/146] packages: add READMEs --- packages/app/README.md | 3 +++ packages/cli/README.md | 22 ++++++++++++++++++++++ packages/core/README.md | 22 ++++++++++++++++++++++ packages/storybook/README.md | 3 +++ 4 files changed, 50 insertions(+) create mode 100644 packages/app/README.md create mode 100644 packages/cli/README.md create mode 100644 packages/core/README.md create mode 100644 packages/storybook/README.md diff --git a/packages/app/README.md b/packages/app/README.md new file mode 100644 index 0000000000..cabf812650 --- /dev/null +++ b/packages/app/README.md @@ -0,0 +1,3 @@ +# @spotify-backstage/app + +This package is an example of a Backstage application. diff --git a/packages/cli/README.md b/packages/cli/README.md new file mode 100644 index 0000000000..99afb635ce --- /dev/null +++ b/packages/cli/README.md @@ -0,0 +1,22 @@ +# @backstage/cli + +This package provides a CLI for developing Backstage plugins and apps. + +## Installation + +Install the package via npm or yarn: + +```sh +$ npm install --save @backstage/cli +``` + +or + +```sh +$ yarn add @backstage/cli +``` + +## Documentation + +- [Backstage Readme](https://github.com/spotify/backstage/blob/master/README.md) +- [Backstage Documentation](https://github.com/spotify/backstage/blob/master/docs/README.md) diff --git a/packages/core/README.md b/packages/core/README.md new file mode 100644 index 0000000000..a22f66ecd9 --- /dev/null +++ b/packages/core/README.md @@ -0,0 +1,22 @@ +# @backstage/core + +This package provides the core API used by Backstage plugins and apps. + +## Installation + +Install the package via npm or yarn: + +```sh +$ npm install --save @backstage/core +``` + +or + +```sh +$ yarn add @backstage/core +``` + +## Documentation + +- [Backstage Readme](https://github.com/spotify/backstage/blob/master/README.md) +- [Backstage Documentation](https://github.com/spotify/backstage/blob/master/docs/README.md) diff --git a/packages/storybook/README.md b/packages/storybook/README.md new file mode 100644 index 0000000000..2e716a5466 --- /dev/null +++ b/packages/storybook/README.md @@ -0,0 +1,3 @@ +# storybook + +This package provides a storybook build for Backstage. See [storybook.backstage.io](http://storybook.backstage.io) From ca992f6cc44d886a7df3ec2ce9ae81e029e019d8 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 25 Mar 2020 12:59:29 +0100 Subject: [PATCH 049/146] packages/{core,cli}: fill in missing package.json fields for publishing --- packages/cli/package.json | 17 +++++++++++++++-- packages/core/package.json | 15 ++++++++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 9cc9ee6aa7..657315d959 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,9 +1,22 @@ { "name": "@spotify-backstage/cli", + "description": "CLI for developing Backstage plugins and apps", "version": "0.1.0", - "main": "dist", - "license": "Apache-2.0", "private": false, + "publishConfig": { + "access": "public" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/spotify/backstage", + "directory": "packages/cli" + }, + "keywords": [ + "backstage" + ], + "license": "Apache-2.0", + "main": "dist", "scripts": { "exec": "npx ts-node ./src", "build": "tsc --outDir dist --noEmit false --module CommonJS", diff --git a/packages/core/package.json b/packages/core/package.json index b6ceeb42f2..d1fb12fdec 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,8 +1,21 @@ { "name": "@spotify-backstage/core", + "description": "Core API used by Backstage plugins and apps", "version": "0.1.0", - "license": "Apache-2.0", "private": false, + "publishConfig": { + "access": "public" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/spotify/backstage", + "directory": "packages/core" + }, + "keywords": [ + "backstage" + ], + "license": "Apache-2.0", "main": "dist/cjs/index.js", "types": "dist/cjs/index.d.ts", "scripts": { From 3a0d8edcb57c25ff23b3e935d5342de2b25b1641 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 25 Mar 2020 19:12:11 +0100 Subject: [PATCH 050/146] rename npm namespace from @spotify-backstage to @backstage --- .eslintrc.js | 6 +++--- docs/getting-started/Plugin development.md | 2 +- docs/getting-started/structure-of-a-plugin.md | 2 +- docs/reference/createPlugin.md | 2 +- package.json | 2 +- packages/app/README.md | 2 +- packages/app/package.json | 10 +++++----- packages/app/src/App.tsx | 2 +- packages/app/src/apis.ts | 2 +- .../app/src/components/ErrorDisplay/ErrorDisplay.tsx | 2 +- packages/app/src/components/Root/Root.tsx | 2 +- packages/app/src/plugins.ts | 4 ++-- packages/cli/package.json | 2 +- .../src/commands/create-plugin/createPlugin.test.ts | 5 ++--- .../cli/src/commands/create-plugin/createPlugin.ts | 6 +++--- packages/cli/src/commands/watch-deps/index.ts | 2 +- packages/cli/templates/default-plugin/package.json.hbs | 4 ++-- .../ExampleComponent/ExampleComponent.test.tsx.hbs | 2 +- .../ExampleComponent/ExampleComponent.tsx.hbs | 2 +- .../ExampleFetchComponent.tsx.hbs | 2 +- .../cli/templates/default-plugin/src/plugin.ts.hbs | 2 +- packages/core/package.json | 4 ++-- plugins/home-page/package.json | 6 +++--- .../src/components/HomePage/HomePage.test.tsx | 2 +- plugins/home-page/src/components/HomePage/HomePage.tsx | 8 +------- .../src/components/HomePage/SquadTechHealth.tsx | 2 +- .../src/components/HomepageTimer/HomepageTimer.tsx | 2 +- plugins/home-page/src/plugin.ts | 2 +- plugins/welcome/package.json | 6 +++--- plugins/welcome/src/components/Timer/Timer.tsx | 2 +- .../src/components/WelcomePage/ErrorButton.test.tsx | 2 +- .../welcome/src/components/WelcomePage/ErrorButton.tsx | 2 +- .../src/components/WelcomePage/WelcomePage.test.tsx | 2 +- .../welcome/src/components/WelcomePage/WelcomePage.tsx | 2 +- plugins/welcome/src/plugin.ts | 2 +- 35 files changed, 51 insertions(+), 58 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index c227c17938..969b6d7d5e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,5 +1,5 @@ -const path = require('path') -const base = require('@spotify-backstage/cli/config/eslint'); +const path = require('path'); +const base = require('@backstage/cli/config/eslint'); module.exports = { ...base, @@ -8,7 +8,7 @@ module.exports = { 'notice/notice': [ 'error', { - templateFile: path.resolve(__dirname, "scripts/copyright.js"), + templateFile: path.resolve(__dirname, 'scripts/copyright.js'), }, ], }, diff --git a/docs/getting-started/Plugin development.md b/docs/getting-started/Plugin development.md index 45cb171ef9..774a02434e 100644 --- a/docs/getting-started/Plugin development.md +++ b/docs/getting-started/Plugin development.md @@ -40,7 +40,7 @@ The app will call the `createPlugin` method on each plugin, passing in a `router of methods on it. ```typescript -import { createPlugin } from '@spotify-backstage/core'; +import { createPlugin } from '@backstage/core'; import ExampleComponent from './components/ExampleComponent'; export default createPlugin({ diff --git a/docs/getting-started/structure-of-a-plugin.md b/docs/getting-started/structure-of-a-plugin.md index c6034261f1..2b31221373 100644 --- a/docs/getting-started/structure-of-a-plugin.md +++ b/docs/getting-started/structure-of-a-plugin.md @@ -43,7 +43,7 @@ In the root folder you have some configuration for typescript and jest, the test In the `src` folder we get to the interesting bits. Check out the `plugin.ts`: ```jsx -import { createPlugin } from '@spotify-backstage/core'; +import { createPlugin } from '@backstage/core'; import ExampleComponent from './components/ExampleComponent'; export default createPlugin({ diff --git a/docs/reference/createPlugin.md b/docs/reference/createPlugin.md index 779e1ef820..77048199ef 100644 --- a/docs/reference/createPlugin.md +++ b/docs/reference/createPlugin.md @@ -26,7 +26,7 @@ type PluginHooks = { Showcasing adding multiple routes and a redirect. ```jsx -import { createPlugin } from '@spotify-backstage/core'; +import { createPlugin } from '@backstage/core'; import ExampleComponent from './components/ExampleComponent'; export default createPlugin({ diff --git a/package.json b/package.json index a25335f659..736da36ac9 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "node": ">=12.0.0" }, "scripts": { - "start": "yarn build && yarn workspace @spotify-backstage/app start", + "start": "yarn build && yarn workspace @backstage/app start", "build": "lerna run build", "test": "cross-env CI=true lerna run test --since origin/master -- --coverage", "create-plugin": "backstage-cli create-plugin", diff --git a/packages/app/README.md b/packages/app/README.md index cabf812650..0e4aa9912e 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -1,3 +1,3 @@ -# @spotify-backstage/app +# @backstage/app This package is an example of a Backstage application. diff --git a/packages/app/package.json b/packages/app/package.json index 4f581599a5..468e2842db 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,15 +1,15 @@ { - "name": "@spotify-backstage/app", + "name": "@backstage/app", "version": "0.0.0", "private": true, "dependencies": { "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", - "@spotify-backstage/cli": "^0.1.0", - "@spotify-backstage/core": "^0.1.0", - "@spotify-backstage/plugin-home-page": "^0.1.0", - "@spotify-backstage/plugin-welcome": "^0.1.0", + "@backstage/cli": "^0.1.0", + "@backstage/core": "^0.1.0", + "@backstage/plugin-home-page": "^0.1.0", + "@backstage/plugin-welcome": "^0.1.0", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.3.2", "@testing-library/user-event": "^7.1.2", diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 91b588041c..09d7f55094 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -15,7 +15,7 @@ */ import { CssBaseline, makeStyles, ThemeProvider } from '@material-ui/core'; -import { BackstageTheme, createApp } from '@spotify-backstage/core'; +import { BackstageTheme, createApp } from '@backstage/core'; import React, { FC } from 'react'; import { BrowserRouter as Router } from 'react-router-dom'; import Root from './components/Root'; diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index 6e2de63795..c3315257d8 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { ApiHolder, ApiRegistry, errorApiRef } from '@spotify-backstage/core'; +import { ApiHolder, ApiRegistry, errorApiRef } from '@backstage/core'; import { ErrorDisplayForwarder } from './components/ErrorDisplay/ErrorDisplay'; const builder = ApiRegistry.builder(); diff --git a/packages/app/src/components/ErrorDisplay/ErrorDisplay.tsx b/packages/app/src/components/ErrorDisplay/ErrorDisplay.tsx index 307158d88c..25e87c7bff 100644 --- a/packages/app/src/components/ErrorDisplay/ErrorDisplay.tsx +++ b/packages/app/src/components/ErrorDisplay/ErrorDisplay.tsx @@ -18,7 +18,7 @@ import React, { FC, useEffect, useState } from 'react'; import PropTypes from 'prop-types'; import { Snackbar } from '@material-ui/core'; import { Alert } from '@material-ui/lab'; -import { ErrorApi, ErrorContext } from '@spotify-backstage/core'; +import { ErrorApi, ErrorContext } from '@backstage/core'; type SubscriberFunc = (error: Error) => void; type Unsubscribe = () => void; diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index 8795b50e1d..920f5c9871 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -28,7 +28,7 @@ import { SidebarSpacer, SidebarDivider, SidebarSpace, -} from '@spotify-backstage/core'; +} from '@backstage/core'; const useSidebarLogoStyles = makeStyles({ root: { diff --git a/packages/app/src/plugins.ts b/packages/app/src/plugins.ts index 12bba3ac06..3c757a59c9 100644 --- a/packages/app/src/plugins.ts +++ b/packages/app/src/plugins.ts @@ -1,3 +1,3 @@ /* eslint-disable notice/notice */ -export { default as HomePagePlugin } from '@spotify-backstage/plugin-home-page'; -export { default as WelcomePlugin } from '@spotify-backstage/plugin-welcome'; +export { default as HomePagePlugin } from '@backstage/plugin-home-page'; +export { default as WelcomePlugin } from '@backstage/plugin-welcome'; diff --git a/packages/cli/package.json b/packages/cli/package.json index 1e85c44c97..bf24095c61 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,5 +1,5 @@ { - "name": "@spotify-backstage/cli", + "name": "@backstage/cli", "description": "CLI for developing Backstage plugins and apps", "version": "0.1.0", "private": false, diff --git a/packages/cli/src/commands/create-plugin/createPlugin.test.ts b/packages/cli/src/commands/create-plugin/createPlugin.test.ts index 1d78cb9191..9bf50027e3 100644 --- a/packages/cli/src/commands/create-plugin/createPlugin.test.ts +++ b/packages/cli/src/commands/create-plugin/createPlugin.test.ts @@ -59,9 +59,8 @@ describe('createPlugin', () => { const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'test-')); try { const sourceData = - '{"name": "@spotify-backstage/{{id}}", "version": "{{version}}"}'; - const targetData = - '{"name": "@spotify-backstage/foo", "version": "0.0.0"}'; + '{"name": "@backstage/{{id}}", "version": "{{version}}"}'; + const targetData = '{"name": "@backstage/foo", "version": "0.0.0"}'; const sourcePath = path.join(tempDir, 'in.hbs'); const targetPath = path.join(tempDir, 'out.json'); fs.writeFileSync(sourcePath, sourceData); diff --git a/packages/cli/src/commands/create-plugin/createPlugin.ts b/packages/cli/src/commands/create-plugin/createPlugin.ts index 112377cbda..4d264b01f9 100644 --- a/packages/cli/src/commands/create-plugin/createPlugin.ts +++ b/packages/cli/src/commands/create-plugin/createPlugin.ts @@ -130,7 +130,7 @@ export const addPluginDependencyToApp = ( console.log(); console.log(chalk.green(' Adding plugin as dependency in app:')); - const pluginPackage = `@spotify-backstage/plugin-${pluginName}`; + const pluginPackage = `@backstage/plugin-${pluginName}`; const packageFile = path.join(rootDir, 'packages', 'app', 'package.json'); process.stdout.write( @@ -171,7 +171,7 @@ export const addPluginToApp = (rootDir: string, pluginName: string) => { console.log(); console.log(chalk.green(' Import plugin in app:')); - const pluginPackage = `@spotify-backstage/plugin-${pluginName}`; + const pluginPackage = `@backstage/plugin-${pluginName}`; const pluginNameCapitalized = pluginName .split('-') .map(name => capitalize(name)) @@ -414,7 +414,7 @@ const createPlugin = async () => { console.log( chalk.green( `🥇 Successfully created ${chalk.cyan( - `@spotify-backstage/plugin-${answers.id}`, + `@backstage/plugin-${answers.id}`, )}`, ), ); diff --git a/packages/cli/src/commands/watch-deps/index.ts b/packages/cli/src/commands/watch-deps/index.ts index e5993ae9d6..059c2a7b5e 100644 --- a/packages/cli/src/commands/watch-deps/index.ts +++ b/packages/cli/src/commands/watch-deps/index.ts @@ -26,7 +26,7 @@ import { waitForExit } from '../../helpers/run'; const PACKAGE_BLACKLIST = [ // We never want to watch for changes in the cli, but all packages will depend on it. - '@spotify-backstage/cli', + '@backstage/cli', ]; const WATCH_LOCATIONS = ['package.json', 'src', 'assets']; diff --git a/packages/cli/templates/default-plugin/package.json.hbs b/packages/cli/templates/default-plugin/package.json.hbs index d8a303184c..6e416f3287 100644 --- a/packages/cli/templates/default-plugin/package.json.hbs +++ b/packages/cli/templates/default-plugin/package.json.hbs @@ -1,5 +1,5 @@ { - "name": "@spotify-backstage/plugin-{{id}}", + "name": "@backstage/plugin-{{id}}", "version": "{{version}}", "main": "dist/cjs/index.js", "types": "dist/cjs/index.d.ts", @@ -11,7 +11,7 @@ "test": "backstage-cli test" }, "devDependencies": { - "@spotify-backstage/cli": "^{{version}}", + "@backstage/cli": "^{{version}}", "@types/testing-library__jest-dom": "5.0.2", "jest-fetch-mock": "^3.0.3" }, diff --git a/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs b/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs index 360055dc3d..15db0863d2 100644 --- a/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs +++ b/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs @@ -19,7 +19,7 @@ import { render } from '@testing-library/react'; import mockFetch from 'jest-fetch-mock'; import ExampleComponent from './ExampleComponent'; import { ThemeProvider } from '@material-ui/core'; -import { BackstageTheme } from '@spotify-backstage/core'; +import { BackstageTheme } from '@backstage/core'; describe('ExampleComponent', () => { it('should render', () => { diff --git a/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.tsx.hbs b/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.tsx.hbs index b5c56422da..b96c970022 100644 --- a/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.tsx.hbs +++ b/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.tsx.hbs @@ -25,7 +25,7 @@ import { ContentHeader, HeaderLabel, SupportButton, -} from '@spotify-backstage/core'; +} from '@backstage/core'; import ExampleFetchComponent from '../ExampleFetchComponent'; const ExampleComponent: FC<{}> = () => ( diff --git a/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx.hbs b/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx.hbs index 4801dea6dd..3420cc9d5a 100644 --- a/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx.hbs +++ b/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx.hbs @@ -25,7 +25,7 @@ import TableHead from '@material-ui/core/TableHead'; import TableRow from '@material-ui/core/TableRow'; import Alert from '@material-ui/lab/Alert'; import { useAsync } from 'react-use'; -import { Progress } from '@spotify-backstage/core'; +import { Progress } from '@backstage/core'; const useStyles = makeStyles({ table: { diff --git a/packages/cli/templates/default-plugin/src/plugin.ts.hbs b/packages/cli/templates/default-plugin/src/plugin.ts.hbs index ccac096007..bb0b93ca25 100644 --- a/packages/cli/templates/default-plugin/src/plugin.ts.hbs +++ b/packages/cli/templates/default-plugin/src/plugin.ts.hbs @@ -14,7 +14,7 @@ * limitations under the License. */ -import { createPlugin } from '@spotify-backstage/core'; +import { createPlugin } from '@backstage/core'; import ExampleComponent from './components/ExampleComponent'; export default createPlugin({ diff --git a/packages/core/package.json b/packages/core/package.json index b18705316c..48bbe83cd6 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,5 +1,5 @@ { - "name": "@spotify-backstage/core", + "name": "@backstage/core", "description": "Core API used by Backstage plugins and apps", "version": "0.1.0", "private": false, @@ -42,7 +42,7 @@ "recompose": "0.30.0" }, "devDependencies": { - "@spotify-backstage/cli": "^0.1.0", + "@backstage/cli": "^0.1.0", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.3.2", "@testing-library/user-event": "^7.1.2", diff --git a/plugins/home-page/package.json b/plugins/home-page/package.json index 5869b5fde0..2116215e94 100644 --- a/plugins/home-page/package.json +++ b/plugins/home-page/package.json @@ -1,12 +1,12 @@ { - "name": "@spotify-backstage/plugin-home-page", + "name": "@backstage/plugin-home-page", "version": "0.1.0", "main": "dist/cjs/index.js", "types": "dist/cjs/index.d.ts", "private": true, "devDependencies": { - "@spotify-backstage/cli": "^0.1.0", - "@spotify-backstage/core": "^0.1.0", + "@backstage/cli": "^0.1.0", + "@backstage/core": "^0.1.0", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.3.2", "@testing-library/user-event": "^7.1.2", diff --git a/plugins/home-page/src/components/HomePage/HomePage.test.tsx b/plugins/home-page/src/components/HomePage/HomePage.test.tsx index 500d53397a..6b2c1bfd11 100644 --- a/plugins/home-page/src/components/HomePage/HomePage.test.tsx +++ b/plugins/home-page/src/components/HomePage/HomePage.test.tsx @@ -18,7 +18,7 @@ import React from 'react'; import { render } from '@testing-library/react'; import HomePage from './HomePage'; import { ThemeProvider } from '@material-ui/core'; -import { BackstageTheme } from '@spotify-backstage/core'; +import { BackstageTheme } from '@backstage/core'; describe('HomePage', () => { it('should render', () => { diff --git a/plugins/home-page/src/components/HomePage/HomePage.tsx b/plugins/home-page/src/components/HomePage/HomePage.tsx index f340b682fa..3ca572b367 100644 --- a/plugins/home-page/src/components/HomePage/HomePage.tsx +++ b/plugins/home-page/src/components/HomePage/HomePage.tsx @@ -17,13 +17,7 @@ import React, { FC } from 'react'; import { Typography, Link, Grid } from '@material-ui/core'; import HomePageTimer from '../HomepageTimer'; -import { - Content, - InfoCard, - Header, - Page, - pageTheme, -} from '@spotify-backstage/core'; +import { Content, InfoCard, Header, Page, pageTheme } from '@backstage/core'; import SquadTechHealth from './SquadTechHealth'; import Table from '@material-ui/core/Table'; import TableBody from '@material-ui/core/TableBody'; diff --git a/plugins/home-page/src/components/HomePage/SquadTechHealth.tsx b/plugins/home-page/src/components/HomePage/SquadTechHealth.tsx index 320205367c..c4e55c4dd5 100644 --- a/plugins/home-page/src/components/HomePage/SquadTechHealth.tsx +++ b/plugins/home-page/src/components/HomePage/SquadTechHealth.tsx @@ -17,7 +17,7 @@ import React, { FC } from 'react'; import { Grid, Typography } from '@material-ui/core'; -import { HorizontalScrollGrid, ProgressCard } from '@spotify-backstage/core'; +import { HorizontalScrollGrid, ProgressCard } from '@backstage/core'; const SquadTechHealth: FC<{}> = () => { return ( diff --git a/plugins/home-page/src/components/HomepageTimer/HomepageTimer.tsx b/plugins/home-page/src/components/HomepageTimer/HomepageTimer.tsx index 3407da4012..770f98e762 100644 --- a/plugins/home-page/src/components/HomepageTimer/HomepageTimer.tsx +++ b/plugins/home-page/src/components/HomepageTimer/HomepageTimer.tsx @@ -15,7 +15,7 @@ */ import React, { FC } from 'react'; -import { HeaderLabel } from '@spotify-backstage/core'; +import { HeaderLabel } from '@backstage/core'; const timeFormat = { hour: '2-digit', minute: '2-digit' }; const utcOptions = { timeZone: 'UTC', ...timeFormat }; diff --git a/plugins/home-page/src/plugin.ts b/plugins/home-page/src/plugin.ts index 0ace719780..17eb89eb35 100644 --- a/plugins/home-page/src/plugin.ts +++ b/plugins/home-page/src/plugin.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { createPlugin } from '@spotify-backstage/core'; +import { createPlugin } from '@backstage/core'; import HomePage from './components/HomePage'; export default createPlugin({ diff --git a/plugins/welcome/package.json b/plugins/welcome/package.json index 734ba4fea6..47d8be7033 100644 --- a/plugins/welcome/package.json +++ b/plugins/welcome/package.json @@ -1,5 +1,5 @@ { - "name": "@spotify-backstage/plugin-welcome", + "name": "@backstage/plugin-welcome", "version": "0.1.0", "main": "dist/cjs/index.js", "types": "dist/cjs/index.d.ts", @@ -11,8 +11,8 @@ "test": "backstage-cli test" }, "devDependencies": { - "@spotify-backstage/core": "^0.1.0", - "@spotify-backstage/cli": "^0.1.0", + "@backstage/core": "^0.1.0", + "@backstage/cli": "^0.1.0", "@types/testing-library__jest-dom": "5.0.2" }, "dependencies": { diff --git a/plugins/welcome/src/components/Timer/Timer.tsx b/plugins/welcome/src/components/Timer/Timer.tsx index 3407da4012..770f98e762 100644 --- a/plugins/welcome/src/components/Timer/Timer.tsx +++ b/plugins/welcome/src/components/Timer/Timer.tsx @@ -15,7 +15,7 @@ */ import React, { FC } from 'react'; -import { HeaderLabel } from '@spotify-backstage/core'; +import { HeaderLabel } from '@backstage/core'; const timeFormat = { hour: '2-digit', minute: '2-digit' }; const utcOptions = { timeZone: 'UTC', ...timeFormat }; diff --git a/plugins/welcome/src/components/WelcomePage/ErrorButton.test.tsx b/plugins/welcome/src/components/WelcomePage/ErrorButton.test.tsx index 791937a3b5..6a91a6c202 100644 --- a/plugins/welcome/src/components/WelcomePage/ErrorButton.test.tsx +++ b/plugins/welcome/src/components/WelcomePage/ErrorButton.test.tsx @@ -17,7 +17,7 @@ import React from 'react'; import { render, fireEvent } from '@testing-library/react'; import ErrorButton from './ErrorButton'; -import { ApiRegistry, errorApiRef, ApiProvider } from '@spotify-backstage/core'; +import { ApiRegistry, errorApiRef, ApiProvider } from '@backstage/core'; describe('ErrorButton', () => { it('should trigger an error', () => { diff --git a/plugins/welcome/src/components/WelcomePage/ErrorButton.tsx b/plugins/welcome/src/components/WelcomePage/ErrorButton.tsx index fa73115d18..390c434633 100644 --- a/plugins/welcome/src/components/WelcomePage/ErrorButton.tsx +++ b/plugins/welcome/src/components/WelcomePage/ErrorButton.tsx @@ -16,7 +16,7 @@ import React, { FC } from 'react'; import { Button } from '@material-ui/core'; -import { errorApiRef, useApi } from '@spotify-backstage/core'; +import { errorApiRef, useApi } from '@backstage/core'; const ErrorButton: FC<{}> = () => { const errorApi = useApi(errorApiRef); diff --git a/plugins/welcome/src/components/WelcomePage/WelcomePage.test.tsx b/plugins/welcome/src/components/WelcomePage/WelcomePage.test.tsx index 9787fa2a9a..b4139e4ed3 100644 --- a/plugins/welcome/src/components/WelcomePage/WelcomePage.test.tsx +++ b/plugins/welcome/src/components/WelcomePage/WelcomePage.test.tsx @@ -23,7 +23,7 @@ import { ApiProvider, ApiRegistry, errorApiRef, -} from '@spotify-backstage/core'; +} from '@backstage/core'; describe('WelcomePage', () => { it('should render', () => { diff --git a/plugins/welcome/src/components/WelcomePage/WelcomePage.tsx b/plugins/welcome/src/components/WelcomePage/WelcomePage.tsx index 755e19e0ff..69a892a95e 100644 --- a/plugins/welcome/src/components/WelcomePage/WelcomePage.tsx +++ b/plugins/welcome/src/components/WelcomePage/WelcomePage.tsx @@ -33,7 +33,7 @@ import { pageTheme, ContentHeader, SupportButton, -} from '@spotify-backstage/core'; +} from '@backstage/core'; import ErrorButton from './ErrorButton'; const WelcomePage: FC<{}> = () => { diff --git a/plugins/welcome/src/plugin.ts b/plugins/welcome/src/plugin.ts index bdc74209a7..20ec41d4c2 100644 --- a/plugins/welcome/src/plugin.ts +++ b/plugins/welcome/src/plugin.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { createPlugin } from '@spotify-backstage/core'; +import { createPlugin } from '@backstage/core'; import WelcomePage from './components/WelcomePage'; export default createPlugin({ From 019032a7a3db535177a21b21c0162413c46e72c2 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 25 Mar 2020 19:13:14 +0100 Subject: [PATCH 051/146] v0.1.1-alpha.0 --- lerna.json | 7 +++++-- packages/app/package.json | 10 +++++----- packages/cli/package.json | 5 +---- packages/core/package.json | 7 ++----- plugins/home-page/package.json | 12 ++++++------ plugins/welcome/package.json | 6 +++--- 6 files changed, 22 insertions(+), 25 deletions(-) diff --git a/lerna.json b/lerna.json index 322929db1d..1002c5f9be 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,9 @@ { - "packages": ["packages/*", "plugins/*"], + "packages": [ + "packages/*", + "plugins/*" + ], "npmClient": "yarn", "useWorkspaces": true, - "version": "0.1.0" + "version": "0.1.1-alpha.0" } diff --git a/packages/app/package.json b/packages/app/package.json index 468e2842db..2aa2c1312e 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,15 +1,15 @@ { "name": "@backstage/app", - "version": "0.0.0", + "version": "0.1.1-alpha.0", "private": true, "dependencies": { + "@backstage/cli": "^0.1.1-alpha.0", + "@backstage/core": "^0.1.1-alpha.0", + "@backstage/plugin-home-page": "^0.1.1-alpha.0", + "@backstage/plugin-welcome": "^0.1.1-alpha.0", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", - "@backstage/cli": "^0.1.0", - "@backstage/core": "^0.1.0", - "@backstage/plugin-home-page": "^0.1.0", - "@backstage/plugin-welcome": "^0.1.0", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.3.2", "@testing-library/user-event": "^7.1.2", diff --git a/packages/cli/package.json b/packages/cli/package.json index bf24095c61..f26b13a421 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/cli", "description": "CLI for developing Backstage plugins and apps", - "version": "0.1.0", + "version": "0.1.1-alpha.0", "private": false, "publishConfig": { "access": "public" @@ -24,9 +24,6 @@ "test": "backstage-cli test", "start": "nodemon ." }, - "publishConfig": { - "access": "public" - }, "devDependencies": { "@types/fs-extra": "^8.1.0", "@types/html-webpack-plugin": "^3.2.2", diff --git a/packages/core/package.json b/packages/core/package.json index 48bbe83cd6..8adb1307fe 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core", "description": "Core API used by Backstage plugins and apps", - "version": "0.1.0", + "version": "0.1.1-alpha.0", "private": false, "publishConfig": { "access": "public" @@ -23,9 +23,6 @@ "lint": "backstage-cli lint", "test": "backstage-cli test" }, - "publishConfig": { - "access": "public" - }, "dependencies": { "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", @@ -42,7 +39,7 @@ "recompose": "0.30.0" }, "devDependencies": { - "@backstage/cli": "^0.1.0", + "@backstage/cli": "^0.1.1-alpha.0", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.3.2", "@testing-library/user-event": "^7.1.2", diff --git a/plugins/home-page/package.json b/plugins/home-page/package.json index 2116215e94..33be457cba 100644 --- a/plugins/home-page/package.json +++ b/plugins/home-page/package.json @@ -1,12 +1,14 @@ { "name": "@backstage/plugin-home-page", - "version": "0.1.0", + "version": "0.1.1-alpha.0", "main": "dist/cjs/index.js", "types": "dist/cjs/index.d.ts", "private": true, "devDependencies": { - "@backstage/cli": "^0.1.0", - "@backstage/core": "^0.1.0", + "@backstage/cli": "^0.1.1-alpha.0", + "@backstage/core": "^0.1.1-alpha.0", + "@material-ui/core": "^4.9.1", + "@material-ui/icons": "^4.9.1", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.3.2", "@testing-library/user-event": "^7.1.2", @@ -14,9 +16,7 @@ "@types/node": "^12.0.0", "@types/testing-library__jest-dom": "5.0.2", "react": "^16.12.0", - "react-dom": "^16.12.0", - "@material-ui/core": "^4.9.1", - "@material-ui/icons": "^4.9.1" + "react-dom": "^16.12.0" }, "scripts": { "build": "backstage-cli plugin:build", diff --git a/plugins/welcome/package.json b/plugins/welcome/package.json index 47d8be7033..b38977afab 100644 --- a/plugins/welcome/package.json +++ b/plugins/welcome/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-welcome", - "version": "0.1.0", + "version": "0.1.1-alpha.0", "main": "dist/cjs/index.js", "types": "dist/cjs/index.d.ts", "license": "Apache-2.0", @@ -11,8 +11,8 @@ "test": "backstage-cli test" }, "devDependencies": { - "@backstage/core": "^0.1.0", - "@backstage/cli": "^0.1.0", + "@backstage/cli": "^0.1.1-alpha.0", + "@backstage/core": "^0.1.1-alpha.0", "@types/testing-library__jest-dom": "5.0.2" }, "dependencies": { From 211787f86e3e3631fdf974f4ebd582aa1b22f0bc Mon Sep 17 00:00:00 2001 From: Mateus Marquezini Date: Wed, 25 Mar 2020 17:23:38 -0300 Subject: [PATCH 052/146] #306 changed the header color --- packages/core/src/layout/Header/Waves.js | 17 ++++++++--------- .../core/src/layout/Page/PageThemeProvider.ts | 2 +- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/core/src/layout/Header/Waves.js b/packages/core/src/layout/Header/Waves.js index 10c992479b..92a1d80c3c 100644 --- a/packages/core/src/layout/Header/Waves.js +++ b/packages/core/src/layout/Header/Waves.js @@ -84,7 +84,6 @@ const Waves = ({ theme }) => { gradientUnits="userSpaceOnUse" > - { y2="241.925" gradientUnits="userSpaceOnUse" > - - + + { y2="-103.806" gradientUnits="userSpaceOnUse" > - - + + { y2="104.375" gradientUnits="userSpaceOnUse" > - - + + { y2="194.894" gradientUnits="userSpaceOnUse" > - - + + diff --git a/packages/core/src/layout/Page/PageThemeProvider.ts b/packages/core/src/layout/Page/PageThemeProvider.ts index 64f8a9f8a7..a767033029 100644 --- a/packages/core/src/layout/Page/PageThemeProvider.ts +++ b/packages/core/src/layout/Page/PageThemeProvider.ts @@ -75,7 +75,7 @@ export const gradients: Record = { colors: ['#69B9FF', '#ACCEEC'], }, teal: { - colors: ['#1F8A77', 'rgba(155, 240, 225, 1.0)'], + colors: ['#005E4D', '#9BF0E1'], }, }; From 67abc7a2c9f138f2f7c905992928fae5c2d0097c Mon Sep 17 00:00:00 2001 From: Mateus Marquezini Date: Thu, 26 Mar 2020 08:28:05 -0300 Subject: [PATCH 053/146] reverted opacity changes --- packages/core/src/layout/Header/Waves.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/core/src/layout/Header/Waves.js b/packages/core/src/layout/Header/Waves.js index 92a1d80c3c..52523834dc 100644 --- a/packages/core/src/layout/Header/Waves.js +++ b/packages/core/src/layout/Header/Waves.js @@ -93,8 +93,8 @@ const Waves = ({ theme }) => { y2="241.925" gradientUnits="userSpaceOnUse" > - - + + { y2="-103.806" gradientUnits="userSpaceOnUse" > - - + + { y2="104.375" gradientUnits="userSpaceOnUse" > - - + + { y2="194.894" gradientUnits="userSpaceOnUse" > - - + + From 7910f8e7f1952cf6b910f90dcf29c807e4b02762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Thu, 26 Mar 2020 14:39:20 +0100 Subject: [PATCH 054/146] Add RFCs to README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 629bfdc376..9fb99531f9 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ We created Backstage about 4 years ago. While our internal version of Backstage - 🐇 **Phase 3:** Ecosystem (later) - Everyone's infrastructure stack is different. By fostering a vibrant community of contributors we hope to provide an ecosystem of Open Source plugins/integrations that allows you to pick the tools that match your stack. -Check out our [Milestones](https://github.com/spotify/backstage/milestones) and how they relate to the 3 Phases outlined above. +Check out our [Milestones](https://github.com/spotify/backstage/milestones) and open [RFCs](https://github.com/spotify/backstage/labels/rfc) how they relate to the three Phases outlined above. Our vision for Backstage is for it to become the trusted standard toolbox (read: UX layer) for the open source infrastructure landscape. Think of it like Kubernetes for developer experience. We realize this is an ambitious goal. We can’t do it alone. If this sounds interesting or you'd like to help us shape our product vision, we'd love to talk. You can email me directly: [alund@spotify.com](mailto:alund@spotify.com). @@ -93,6 +93,7 @@ Then open http://localhost/ on your browser. - [Discord chat](https://discord.gg/MUpMjP2) - Get support or discuss the project - [Good First Issues](https://github.com/spotify/backstage/labels/good%20first%20issue) - Start here if you want to contribute +- [RFCs](https://github.com/spotify/backstage/labels/rfc) - Help shape the technical direction - [FAQ](docs/FAQ.md) - Frequently Asked Questions - [Code of Conduct](CODE_OF_CONDUCT.md) - This is how we roll - Give us a star ⭐️ - If you are using Backstage or think it is an interesting project, we would love a star ❤️ From 8c1a2765e0324a514cc37c8a1a7c54cf5dd8551c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Thu, 26 Mar 2020 20:27:25 +0100 Subject: [PATCH 055/146] Add question to FAQ --- docs/FAQ.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/FAQ.md b/docs/FAQ.md index 9b9fff564e..4e742fbd72 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -31,3 +31,10 @@ Bitbucket. We believe that in time there will be plugins that will provide funct as well. Hopefully contributed by the community. And note that implementations of Backstage can be hosted wherever you feel suits your needs best. + +## Is Backstage only suitable for developer portals? + +No, not really. The core frontend framework could be used for building any large-scale web application where multiple teams are building separate parts of the app, but you want the overall experience to be consistent. + +That being said, in [Phase 2](https://github.com/spotify/backstage#project-roadmap) of the project we will add features that are needed for developer portals and systems for managing software ecosystems. Our ambition will be to keep Backstage modular. + From abd3caf246039a2824bf1f9e31453145b2535910 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Thu, 26 Mar 2020 20:37:32 +0100 Subject: [PATCH 056/146] Update FAQ.md --- docs/FAQ.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/FAQ.md b/docs/FAQ.md index 4e742fbd72..01c3a4f473 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -32,9 +32,11 @@ as well. Hopefully contributed by the community. And note that implementations of Backstage can be hosted wherever you feel suits your needs best. -## Is Backstage only suitable for developer portals? +## Can Backstage by used for other things than developer portals? -No, not really. The core frontend framework could be used for building any large-scale web application where multiple teams are building separate parts of the app, but you want the overall experience to be consistent. +Yes. + +The core frontend framework could be used for building any large-scale web application where multiple teams are building separate parts of the app, but you want the overall experience to be consistent. That being said, in [Phase 2](https://github.com/spotify/backstage#project-roadmap) of the project we will add features that are needed for developer portals and systems for managing software ecosystems. Our ambition will be to keep Backstage modular. From 580f30f72116995eb49a494e24f823c8436ee2be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Fri, 27 Mar 2020 07:59:12 +0100 Subject: [PATCH 057/146] Link to Blog from README (#397) * Link to Blog from README * Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9fb99531f9..1c20ec8cc6 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,7 @@ Then open http://localhost/ on your browser. - [RFCs](https://github.com/spotify/backstage/labels/rfc) - Help shape the technical direction - [FAQ](docs/FAQ.md) - Frequently Asked Questions - [Code of Conduct](CODE_OF_CONDUCT.md) - This is how we roll +- [Blog](https://backstage.io/blog/) - Announcements and updates - Give us a star ⭐️ - If you are using Backstage or think it is an interesting project, we would love a star ❤️ Or, if you are an open source developer and are interested in joining our team, please reach out to [foss-opportunities@spotify.com ](mailto:foss-opportunities@spotify.com) From a17f237b752508cd7ef58e16e8e2eba383e49977 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Fri, 27 Mar 2020 13:42:27 +0100 Subject: [PATCH 058/146] Add data-testid to buttons used by e2e tests --- .../components/ErrorDisplay/ErrorDisplay.tsx | 17 +++++++++++++++-- .../core/src/api/app/LoginPage/LoginPage.tsx | 13 ++++++++----- .../components/SupportButton/SupportButton.tsx | 6 +++++- packages/core/src/layout/Sidebar/Items.tsx | 2 +- .../src/components/WelcomePage/ErrorButton.tsx | 7 ++++++- 5 files changed, 35 insertions(+), 10 deletions(-) diff --git a/packages/app/src/components/ErrorDisplay/ErrorDisplay.tsx b/packages/app/src/components/ErrorDisplay/ErrorDisplay.tsx index 25e87c7bff..ef24821508 100644 --- a/packages/app/src/components/ErrorDisplay/ErrorDisplay.tsx +++ b/packages/app/src/components/ErrorDisplay/ErrorDisplay.tsx @@ -16,7 +16,8 @@ import React, { FC, useEffect, useState } from 'react'; import PropTypes from 'prop-types'; -import { Snackbar } from '@material-ui/core'; +import { Snackbar, IconButton } from '@material-ui/core'; +import CloseIcon from '@material-ui/icons/Close'; import { Alert } from '@material-ui/lab'; import { ErrorApi, ErrorContext } from '@backstage/core'; @@ -73,7 +74,19 @@ const ErrorDisplay: FC = ({ forwarder }) => { message={firstError.toString()} anchorOrigin={{ vertical: 'top', horizontal: 'center' }} > - + + + + } + severity="error" + > {firstError.toString()} diff --git a/packages/core/src/api/app/LoginPage/LoginPage.tsx b/packages/core/src/api/app/LoginPage/LoginPage.tsx index 4f8214b35c..7352600f63 100644 --- a/packages/core/src/api/app/LoginPage/LoginPage.tsx +++ b/packages/core/src/api/app/LoginPage/LoginPage.tsx @@ -27,7 +27,7 @@ import { TextField, List, ListItem, - Link + Link, } from '@material-ui/core'; import InfoCard from '../../../layout/InfoCard/InfoCard'; @@ -37,7 +37,9 @@ enum AuthType { const LoginPage: FC<{}> = () => { const [githubUsername, setGithubUsername] = useState(String); - const [githubPersonalAuthToken, setGithubPersonalAuthToken] = useState(String); + const [githubPersonalAuthToken, setGithubPersonalAuthToken] = useState( + String, + ); const [loginDetails, setLoginDetails] = useState(Object); const saveGithubInfo = (info: {}) => { @@ -70,11 +72,11 @@ const LoginPage: FC<{}> = () => { 'Content-Type': 'application/x-www-form-urlencoded', }), }) - .then((response) => { + .then(response => { if (response.status === 200) return response.json(); throw Error(`${response.status} ${response.statusText}`); }) - .then((data) => { + .then(data => { const info = { username: username, token: token, @@ -158,6 +160,7 @@ const LoginPage: FC<{}> = () => { diff --git a/packages/core/src/layout/Sidebar/Items.tsx b/packages/core/src/layout/Sidebar/Items.tsx index ddce3b4301..87a3038a55 100644 --- a/packages/core/src/layout/Sidebar/Items.tsx +++ b/packages/core/src/layout/Sidebar/Items.tsx @@ -94,7 +94,7 @@ export const SidebarItem: FC = ({ onClick={onClick} underline="none" > -
+
diff --git a/plugins/welcome/src/components/WelcomePage/ErrorButton.tsx b/plugins/welcome/src/components/WelcomePage/ErrorButton.tsx index 390c434633..daaf4c94ac 100644 --- a/plugins/welcome/src/components/WelcomePage/ErrorButton.tsx +++ b/plugins/welcome/src/components/WelcomePage/ErrorButton.tsx @@ -26,7 +26,12 @@ const ErrorButton: FC<{}> = () => { }; return ( - ); From b0329a82612ab6728caf105bae501e4e67c68e1f Mon Sep 17 00:00:00 2001 From: Paul Marbach Date: Fri, 27 Mar 2020 09:26:31 -0400 Subject: [PATCH 059/146] fix: install @backstage/core when scaffolding a plugin --- packages/cli/templates/default-plugin/package.json.hbs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/cli/templates/default-plugin/package.json.hbs b/packages/cli/templates/default-plugin/package.json.hbs index 6e416f3287..b2bd6f2c82 100644 --- a/packages/cli/templates/default-plugin/package.json.hbs +++ b/packages/cli/templates/default-plugin/package.json.hbs @@ -16,6 +16,7 @@ "jest-fetch-mock": "^3.0.3" }, "dependencies": { + "@backstage/core": "^{{version}}", "@material-ui/lab": "4.0.0-alpha.45" } } From 990671df9b9eeb516c901024c06ebc5de2f90100 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Fri, 27 Mar 2020 14:07:26 +0100 Subject: [PATCH 060/146] Add e2e test for app with cypress --- packages/app/cypress.json | 5 + packages/app/cypress/.eslintrc.json | 12 + packages/app/cypress/integration/app.js | 63 +++ packages/app/cypress/support/index.js | 16 + packages/app/package.json | 14 +- yarn.lock | 521 +++++++++++++++++++++--- 6 files changed, 575 insertions(+), 56 deletions(-) create mode 100644 packages/app/cypress.json create mode 100644 packages/app/cypress/.eslintrc.json create mode 100644 packages/app/cypress/integration/app.js create mode 100644 packages/app/cypress/support/index.js diff --git a/packages/app/cypress.json b/packages/app/cypress.json new file mode 100644 index 0000000000..6ae01d9c2a --- /dev/null +++ b/packages/app/cypress.json @@ -0,0 +1,5 @@ +{ + "baseUrl": "http://localhost:3000", + "fixturesFolder": false, + "pluginsFile": false +} diff --git a/packages/app/cypress/.eslintrc.json b/packages/app/cypress/.eslintrc.json new file mode 100644 index 0000000000..a467608916 --- /dev/null +++ b/packages/app/cypress/.eslintrc.json @@ -0,0 +1,12 @@ +{ + "plugins": ["cypress"], + "extends": ["plugin:cypress/recommended"], + "rules": { + "jest/expect-expect": [ + "error", + { + "assertFunctionNames": ["expect", "cy.contains"] + } + ] + } +} diff --git a/packages/app/cypress/integration/app.js b/packages/app/cypress/integration/app.js new file mode 100644 index 0000000000..a7462588a0 --- /dev/null +++ b/packages/app/cypress/integration/app.js @@ -0,0 +1,63 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +describe('App', () => { + it('should render the welcome page', () => { + cy.visit('/'); + cy.contains('Welcome to Backstage'); + cy.contains('Getting Started'); + cy.contains('Quick Links'); + cy.contains('APIs'); + }); + + it('should display support info when clicking the button', () => { + cy.visit('/'); + cy.findByTestId('support-button').click({ force: true }); + cy.contains('#backstage'); + }); + + it('should display error message when triggering it', () => { + cy.visit('/'); + cy.findByTestId('error-button').click({ force: true }); + cy.contains('Error: Oh no!'); + cy.findByTestId('error-button-close').click({ force: true }); + }); + + it('should be able to login and logout', () => { + const name = 'test-name'; + Cypress.on('window:before:load', win => { + win.fetch = cy.stub().resolves({ + status: 200, + json: () => ({ username: 'test name', token: 'token', name }), + }); + }); + + cy.visit('/'); + cy.get('a[href="/login"]').click({ force: true }); + cy.url().should('include', '/login'); + cy.contains('Welcome, guest!'); + cy.contains('Username') + .get('input[name=github-username-tf]') + .type(name, { force: true }); + cy.contains('Token') + .get('input[name=github-auth-tf]') + .type('password', { force: true }); + cy.findByTestId('github-auth-button').click({ force: true }); + cy.contains(`Welcome, ${name}!`); + cy.contains('Logout').click({ force: true }); + cy.contains('Welcome, guest!'); + }); +}); diff --git a/packages/app/cypress/support/index.js b/packages/app/cypress/support/index.js new file mode 100644 index 0000000000..8fc8ca91f1 --- /dev/null +++ b/packages/app/cypress/support/index.js @@ -0,0 +1,16 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import '@testing-library/cypress/add-commands'; diff --git a/packages/app/package.json b/packages/app/package.json index 2aa2c1312e..9babc5f17b 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -28,7 +28,11 @@ "start": "backstage-cli app:serve", "build": "backstage-cli app:build", "test": "backstage-cli test", - "lint": "backstage-cli lint" + "test:e2e": "start-server-and-test start http://localhost:3000 cy:dev", + "test:e2e:ci": "start-server-and-test start http://localhost:3000 cy:run", + "lint": "backstage-cli lint", + "cy:dev": "cypress open", + "cy:run": "cypress run" }, "browserslist": { "production": [ @@ -42,5 +46,11 @@ "last 1 safari version" ] }, - "license": "Apache-2.0" + "license": "Apache-2.0", + "devDependencies": { + "@testing-library/cypress": "^6.0.0", + "@types/jquery": "^3.3.34", + "cypress": "^4.2.0", + "start-server-and-test": "^1.10.11" + } } diff --git a/yarn.lock b/yarn.lock index 78f006747e..588f8e6956 100644 --- a/yarn.lock +++ b/yarn.lock @@ -919,7 +919,7 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.4", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.4", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.9.2" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz#d90df0583a3a252f09aaa619665367bae518db06" integrity sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q== @@ -1112,6 +1112,24 @@ resolved "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-10.1.0.tgz#f0950bba18819512d42f7197e56c518aa491cf18" integrity sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg== +"@cypress/listr-verbose-renderer@0.4.1": + version "0.4.1" + resolved "https://registry.npmjs.org/@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/xvfb@1.2.4": + version "1.2.4" + resolved "https://registry.npmjs.org/@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" + "@emotion/cache@^10.0.27": version "10.0.29" resolved "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz#87e7e64f412c060102d589fe7c6dc042e6f9d1e0" @@ -1288,7 +1306,7 @@ unique-filename "^1.1.1" which "^1.3.1" -"@hapi/address@2.x.x": +"@hapi/address@2.x.x", "@hapi/address@^2.1.2": version "2.1.4" resolved "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5" integrity sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ== @@ -1298,7 +1316,12 @@ resolved "https://registry.npmjs.org/@hapi/bourne/-/bourne-1.3.2.tgz#0a7095adea067243ce3283e1b56b8a8f453b242a" integrity sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA== -"@hapi/hoek@8.x.x", "@hapi/hoek@^8.3.0": +"@hapi/formula@^1.2.0": + version "1.2.0" + resolved "https://registry.npmjs.org/@hapi/formula/-/formula-1.2.0.tgz#994649c7fea1a90b91a0a1e6d983523f680e10cd" + integrity sha512-UFbtbGPjstz0eWHb+ga/GM3Z9EzqKXFWIbSOFURU0A/Gku0Bky4bCk9/h//K2Xr3IrCfjFNhMm4jyZ5dbCewGA== + +"@hapi/hoek@8.x.x", "@hapi/hoek@^8.2.4", "@hapi/hoek@^8.3.0": version "8.5.1" resolved "https://registry.npmjs.org/@hapi/hoek/-/hoek-8.5.1.tgz#fde96064ca446dec8c55a8c2f130957b070c6e06" integrity sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow== @@ -1313,7 +1336,23 @@ "@hapi/hoek" "8.x.x" "@hapi/topo" "3.x.x" -"@hapi/topo@3.x.x": +"@hapi/joi@^16.1.8": + version "16.1.8" + resolved "https://registry.npmjs.org/@hapi/joi/-/joi-16.1.8.tgz#84c1f126269489871ad4e2decc786e0adef06839" + integrity sha512-wAsVvTPe+FwSrsAurNt5vkg3zo+TblvC5Bb1zMVK6SJzZqw9UrJnexxR+76cpePmtUZKHAPxcQ2Bf7oVHyahhg== + dependencies: + "@hapi/address" "^2.1.2" + "@hapi/formula" "^1.2.0" + "@hapi/hoek" "^8.2.4" + "@hapi/pinpoint" "^1.0.2" + "@hapi/topo" "^3.1.3" + +"@hapi/pinpoint@^1.0.2": + version "1.0.2" + resolved "https://registry.npmjs.org/@hapi/pinpoint/-/pinpoint-1.0.2.tgz#025b7a36dbbf4d35bf1acd071c26b20ef41e0d13" + integrity sha512-dtXC/WkZBfC5vxscazuiJ6iq4j9oNx1SHknmIr8hofarpKUZKmlUVYVIhNVzIEgK5Wrc4GMHL5lZtt1uS2flmQ== + +"@hapi/topo@3.x.x", "@hapi/topo@^3.1.3": version "3.1.6" resolved "https://registry.npmjs.org/@hapi/topo/-/topo-3.1.6.tgz#68d935fa3eae7fdd5ab0d7f953f3205d8b2bfc29" integrity sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ== @@ -3241,6 +3280,15 @@ "@svgr/plugin-svgo" "^4.3.1" loader-utils "^1.2.3" +"@testing-library/cypress@^6.0.0": + version "6.0.0" + resolved "https://registry.npmjs.org/@testing-library/cypress/-/cypress-6.0.0.tgz#935f7716e0e495f02fd753a42621e4d350097dce" + integrity sha512-vWPQtPsIDk5STOH2XdJbJoYq9gxOSAItP0ail+MlylK230zNkf3ODKd6eqWnDdruuqrhTF3CyqvPNMA8Xks/UQ== + dependencies: + "@babel/runtime" "^7.8.7" + "@testing-library/dom" "^7.0.2" + "@types/testing-library__cypress" "^5.0.3" + "@testing-library/dom@^6.15.0": version "6.16.0" resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-6.16.0.tgz#04ada27ed74ad4c0f0d984a1245bb29b1fd90ba9" @@ -3254,6 +3302,17 @@ pretty-format "^25.1.0" wait-for-expect "^3.0.2" +"@testing-library/dom@^7.0.2": + version "7.1.2" + resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-7.1.2.tgz#0942e3751beeea9820e14dd4bf685f1f1767353a" + integrity sha512-U0wLMbND1NUMUB65E9VmfuehT1GUSIHnT2zK7rjpDIdFNDbMtjDzbdaZXBYDp5lWzHJwUdPQozMd1GHp3O9gow== + dependencies: + "@babel/runtime" "^7.9.2" + "@types/testing-library__dom" "^7.0.0" + aria-query "^4.0.2" + dom-accessibility-api "^0.4.2" + pretty-format "^25.1.0" + "@testing-library/jest-dom@^4.2.4": version "4.2.4" resolved "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-4.2.4.tgz#00dfa0cbdd837d9a3c2a7f3f0a248ea6e7b89742" @@ -3526,6 +3585,13 @@ dependencies: jest-diff "^24.3.0" +"@types/jquery@^3.3.34": + version "3.3.34" + resolved "https://registry.npmjs.org/@types/jquery/-/jquery-3.3.34.tgz#0d3b94057063d3854adaeb579652048fec07ba6c" + integrity sha512-lW9vsVL53Xu/Nj4gi2hNmHGc4u3KKghjqTkAlO0kF5GIOPxbqqnQpgqJBzmn3yXLrPqHb6cmNJ6URnS23Vtvbg== + dependencies: + "@types/sizzle" "*" + "@types/js-cookie@2.2.5": version "2.2.5" resolved "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.5.tgz#38dfaacae8623b37cc0b0d27398e574e3fc28b1e" @@ -3690,6 +3756,11 @@ "@types/express-serve-static-core" "*" "@types/mime" "*" +"@types/sizzle@*", "@types/sizzle@2.3.2": + version "2.3.2" + resolved "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47" + integrity sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg== + "@types/source-list-map@*": version "0.1.2" resolved "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" @@ -3705,6 +3776,14 @@ resolved "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.5.tgz#9adbc12950582aa65ead76bffdf39fe0c27a3c02" integrity sha512-/gG2M/Imw7cQFp8PGvz/SwocNrmKFjFsm5Pb8HdbHkZ1K8pmuPzOX4VeVoiEecFCVf4CsN1r3/BRvx+6sNqwtQ== +"@types/testing-library__cypress@^5.0.3": + version "5.0.3" + resolved "https://registry.npmjs.org/@types/testing-library__cypress/-/testing-library__cypress-5.0.3.tgz#94969b7c1eea96e09d8e023a1d225590fa75a1fe" + integrity sha512-efMwaVnsWEBDz0Ikm84Kh/oiUxMfz5LQtWx/Y6s2Bj0KkQ6+569/101X3Gz2bJy+otaXWPzOUOwDeAfzOJWoeQ== + dependencies: + "@types/testing-library__dom" "*" + cypress "*" + "@types/testing-library__dom@*", "@types/testing-library__dom@^6.12.1": version "6.14.0" resolved "https://registry.npmjs.org/@types/testing-library__dom/-/testing-library__dom-6.14.0.tgz#1aede831cb4ed4a398448df5a2c54b54a365644e" @@ -3712,6 +3791,13 @@ dependencies: pretty-format "^24.3.0" +"@types/testing-library__dom@^7.0.0": + version "7.0.0" + resolved "https://registry.npmjs.org/@types/testing-library__dom/-/testing-library__dom-7.0.0.tgz#c0fb7d1c2495a3d26f19342102142d47500f0319" + integrity sha512-1TEPWyqQ6IQ7R1hCegZmFSA3KrBQjdzJW7yC9ybpRcFst5XuPOqBGNr0mTAKbxwI/TrTyc1skeyLJrpcvAf93w== + dependencies: + pretty-format "^25.1.0" + "@types/testing-library__jest-dom@5.0.2": version "5.0.2" resolved "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.0.2.tgz#89b782e0f187fe1e80d6375133da74182ba02065" @@ -4309,6 +4395,11 @@ aproba@^1.0.3, aproba@^1.1.1, aproba@^1.1.2: resolved "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== +arch@2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/arch/-/arch-2.1.1.tgz#8f5c2731aa35a30929221bb0640eed65175ec84e" + integrity sha512-BLM56aPo9vLLFVa8+/+pJLnrZ7QGGTVHWsCwieAWT9o9K8UeGaQbzZbGoabWLOo2ksBCztoXdqBZBplqLDDCSg== + archy@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" @@ -4551,6 +4642,11 @@ async@^2.6.2: dependencies: lodash "^4.17.14" +async@^3.1.0: + version "3.2.0" + resolved "https://registry.npmjs.org/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720" + integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -5116,7 +5212,7 @@ bindings@^1.5.0: dependencies: file-uri-to-path "1.0.0" -bluebird@^3.3.5, bluebird@^3.5.1, bluebird@^3.5.3, bluebird@^3.5.5: +bluebird@3.7.2, bluebird@^3.3.5, bluebird@^3.5.1, bluebird@^3.5.3, bluebird@^3.5.5: version "3.7.2" resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== @@ -5341,6 +5437,11 @@ btoa-lite@^1.0.0: resolved "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz#337766da15801210fdd956c22e9c6891ab9d0337" integrity sha1-M3dm2hWAEhD92VbCLpxokaudAzc= +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= + buffer-from@1.x, buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" @@ -5460,6 +5561,11 @@ cachedir@2.2.0: resolved "https://registry.npmjs.org/cachedir/-/cachedir-2.2.0.tgz#19afa4305e05d79e417566882e0c8f960f62ff0e" integrity sha512-VvxA0xhNqIIfg0V9AmJkDg91DaJwryutH5rVEZAhcNi4iJFj9f+QxmAjgK1LT9I8OgToX27fypX6/MeCXVbBjQ== +cachedir@2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz#0c75892a052198f0b21c7c1804d8331edfcae0e8" + integrity sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw== + call-limit@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/call-limit/-/call-limit-1.1.1.tgz#ef15f2670db3f1992557e2d965abc459e6e358d4" @@ -5650,6 +5756,11 @@ chardet@^0.7.0: resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== +check-more-types@2.24.0: + version "2.24.0" + resolved "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" + integrity sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA= + chokidar@^2.0.2, chokidar@^2.0.4, chokidar@^2.1.8: version "2.1.8" resolved "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" @@ -5766,6 +5877,13 @@ cli-columns@^3.1.2: string-width "^2.0.0" strip-ansi "^3.0.1" +cli-cursor@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/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.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" @@ -6005,6 +6123,11 @@ commander@2.17.x: resolved "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== +commander@4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/commander/-/commander-4.1.0.tgz#545983a0603fe425bc672d66c9e3c89c42121a83" + integrity sha512-NIQrwvv9V39FHgGFm36+U9SMQzbiHvU79k+iADraJTpmrFFfx7Ds0IvDoAdZsDrknlkRk14OYoWXb57uTh7/sw== + commander@^2.11.0, commander@^2.19.0, commander@^2.20.0, commander@~2.20.3: version "2.20.3" resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -6041,7 +6164,7 @@ commitizen@^4.0.3: strip-bom "4.0.0" strip-json-comments "3.0.1" -common-tags@^1.8.0: +common-tags@1.8.0, common-tags@^1.8.0: version "1.8.0" resolved "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937" integrity sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw== @@ -6096,7 +6219,7 @@ concat-map@0.0.1: resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@^1.5.0: +concat-stream@1.6.2, concat-stream@^1.5.0: version "1.6.2" resolved "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== @@ -6778,6 +6901,48 @@ cyclist@^1.0.1: resolved "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= +cypress@*, cypress@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/cypress/-/cypress-4.2.0.tgz#45673fb648b1a77b9a78d73e58b89ed05212d243" + integrity sha512-8LdreL91S/QiTCLYLNbIjLL8Ht4fJmu/4HGLxUI20Tc7JSfqEfCmXELrRfuPT0kjosJwJJZacdSji9XSRkPKUw== + dependencies: + "@cypress/listr-verbose-renderer" "0.4.1" + "@cypress/xvfb" "1.2.4" + "@types/sizzle" "2.3.2" + arch "2.1.1" + bluebird "3.7.2" + cachedir "2.3.0" + chalk "2.4.2" + check-more-types "2.24.0" + cli-table3 "0.5.1" + commander "4.1.0" + common-tags "1.8.0" + debug "4.1.1" + eventemitter2 "4.1.2" + execa "1.0.0" + executable "4.1.1" + extract-zip "1.6.7" + fs-extra "8.1.0" + getos "3.1.4" + is-ci "2.0.0" + is-installed-globally "0.1.0" + lazy-ass "1.6.0" + listr "0.14.3" + lodash "4.17.15" + log-symbols "3.0.0" + minimist "1.2.2" + moment "2.24.0" + ospath "1.2.2" + pretty-bytes "5.3.0" + ramda "0.26.1" + request cypress-io/request#b5af0d1fa47eec97ba980cde90a13e69a2afcd16 + request-progress "3.0.0" + supports-color "7.1.0" + tmp "0.1.0" + untildify "4.0.0" + url "0.11.0" + yauzl "2.10.0" + cz-conventional-changelog@3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/cz-conventional-changelog/-/cz-conventional-changelog-3.0.1.tgz#b1f207ae050355e7ada65aad5c52e9de3d0c8e5b" @@ -6872,7 +7037,7 @@ debug@3.1.0: dependencies: ms "2.0.0" -debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: +debug@4, debug@4.1.1, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== @@ -7197,6 +7362,11 @@ dom-accessibility-api@^0.3.0: resolved "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.3.0.tgz#511e5993dd673b97c87ea47dba0e3892f7e0c983" integrity sha512-PzwHEmsRP3IGY4gv/Ug+rMeaTIyTJvadCb+ujYXYeIylbHJezIyNToe8KfEgHTCEYyC+/bUghYOGg8yMGlZ6vA== +dom-accessibility-api@^0.4.2: + version "0.4.3" + resolved "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.4.3.tgz#93ca9002eb222fd5a343b6e5e6b9cf5929411c4c" + integrity sha512-JZ8iPuEHDQzq6q0k7PKMGbrIdsgBB7TRrtVOUm4nSMCExlg5qQG4KXWTH2k90yggjM4tTumRGwTKJSldMzKyLA== + dom-converter@^0.2: version "0.2.0" resolved "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" @@ -7345,7 +7515,7 @@ duplexer3@^0.1.4: resolved "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= -duplexer@^0.1.1: +duplexer@^0.1.1, duplexer@~0.1.1: version "0.1.1" resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E= @@ -7877,6 +8047,24 @@ etag@~1.8.1: resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= +event-stream@=3.3.4: + version "3.3.4" + resolved "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" + integrity sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE= + dependencies: + duplexer "~0.1.1" + from "~0" + map-stream "~0.1.0" + pause-stream "0.0.11" + split "0.3" + stream-combiner "~0.0.4" + through "~2.3.1" + +eventemitter2@4.1.2: + version "4.1.2" + resolved "https://registry.npmjs.org/eventemitter2/-/eventemitter2-4.1.2.tgz#0e1a8477af821a6ef3995b311bf74c23a5247f15" + integrity sha1-DhqEd6+CGm7zmVsxG/dMI6UkfxU= + eventemitter3@^3.1.0: version "3.1.2" resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" @@ -7912,20 +8100,7 @@ exec-sh@^0.3.2: resolved "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz#3a018ceb526cc6f6df2bb504b2bfe8e3a4934ec5" integrity sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A== -execa@^0.7.0: - version "0.7.0" - resolved "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" - integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c= - dependencies: - cross-spawn "^5.0.1" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -execa@^1.0.0: +execa@1.0.0, execa@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== @@ -7938,7 +8113,7 @@ execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -execa@^3.2.0, execa@^3.4.0: +execa@3.4.0, execa@^3.2.0, execa@^3.4.0: version "3.4.0" resolved "https://registry.npmjs.org/execa/-/execa-3.4.0.tgz#c08ed4550ef65d858fac269ffc8572446f37eb89" integrity sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g== @@ -7954,6 +8129,19 @@ execa@^3.2.0, execa@^3.4.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +execa@^0.7.0: + version "0.7.0" + resolved "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" + integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c= + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + execa@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/execa/-/execa-4.0.0.tgz#7f37d6ec17f09e6b8fc53288611695b6d12b9daf" @@ -7969,6 +8157,18 @@ execa@^4.0.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +executable@4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/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.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" + integrity sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g= + exit@^0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -8104,6 +8304,16 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" +extract-zip@1.6.7: + version "1.6.7" + resolved "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz#a840b4b8af6403264c8db57f4f1a74333ef81fe9" + integrity sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k= + dependencies: + concat-stream "1.6.2" + debug "2.6.9" + mkdirp "0.5.1" + yauzl "2.4.1" + extsprintf@1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" @@ -8216,6 +8426,20 @@ fbjs@^0.8.1: setimmediate "^1.0.5" ua-parser-js "^0.7.18" +fd-slicer@~1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65" + integrity sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU= + dependencies: + pend "~1.2.0" + +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= + dependencies: + pend "~1.2.0" + figgy-pudding@^3.4.1, figgy-pudding@^3.5.1: version "3.5.1" resolved "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" @@ -8553,6 +8777,11 @@ from2@^2.1.0, from2@^2.3.0: inherits "^2.0.1" readable-stream "^2.0.0" +from@~0: + version "0.1.7" + resolved "https://registry.npmjs.org/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" + integrity sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4= + fs-extra@8.1.0, fs-extra@^8.0.1, fs-extra@^8.1.0: version "8.1.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" @@ -8792,6 +9021,13 @@ get-value@^2.0.3, get-value@^2.0.6: resolved "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= +getos@3.1.4: + version "3.1.4" + resolved "https://registry.npmjs.org/getos/-/getos-3.1.4.tgz#29cdf240ed10a70c049add7b6f8cb08c81876faf" + integrity sha512-UORPzguEB/7UG5hqiZai8f0vQ7hzynMQyJLxStoQ8dPGAcmgsfXOPA4iE/fGtweHYkK+z4zc9V0g+CIFRf5HYw== + dependencies: + async "^3.1.0" + getpass@^0.1.1: version "0.1.7" resolved "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -9987,6 +10223,13 @@ is-callable@^1.1.4, is-callable@^1.1.5: resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q== +is-ci@2.0.0, is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + is-ci@^1.0.10: version "1.2.1" resolved "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz#e3779c8ee17fccf428488f6e281187f2e632841c" @@ -9994,13 +10237,6 @@ is-ci@^1.0.10: dependencies: ci-info "^1.5.0" -is-ci@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" - integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== - dependencies: - ci-info "^2.0.0" - is-cidr@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/is-cidr/-/is-cidr-3.1.0.tgz#72e233d8e1c4cd1d3f11713fcce3eba7b0e3476f" @@ -10165,7 +10401,7 @@ is-in-browser@^1.0.2, is-in-browser@^1.1.3: resolved "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835" integrity sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU= -is-installed-globally@^0.1.0: +is-installed-globally@0.1.0, is-installed-globally@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80" integrity sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA= @@ -11661,6 +11897,11 @@ latest-version@^3.0.0: dependencies: package-json "^4.0.0" +lazy-ass@1.6.0: + version "1.6.0" + resolved "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" + integrity sha1-eZllXoZGwX8In90YfRUNMyTVRRM= + lazy-cache@^0.2.3: version "0.2.7" resolved "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz#7feddf2dcb6edb77d11ef1d117ab5ffdf0ab1b65" @@ -11937,7 +12178,7 @@ listr-verbose-renderer@^0.5.0: date-fns "^1.27.2" figures "^2.0.0" -listr@^0.14.3: +listr@0.14.3, listr@^0.14.3: version "0.14.3" resolved "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz#2fea909604e434be464c50bddba0d496928fa586" integrity sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA== @@ -12147,6 +12388,11 @@ lodash.memoize@4.x, lodash.memoize@^4.1.2: resolved "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= +lodash.once@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= + lodash.set@^4.3.2: version "4.3.2" resolved "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" @@ -12207,6 +12453,13 @@ lodash@4.17.15, "lodash@>=3.5 <5", lodash@^4.17.10, lodash@^4.17.11, lodash@^4.1 resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== +log-symbols@3.0.0, log-symbols@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" + integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ== + dependencies: + chalk "^2.4.2" + log-symbols@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" @@ -12214,13 +12467,6 @@ log-symbols@^1.0.2: dependencies: chalk "^1.0.0" -log-symbols@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" - integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ== - dependencies: - chalk "^2.4.2" - log-update@^2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz#88328fd7d1ce7938b29283746f0b1bc126b24708" @@ -12390,6 +12636,11 @@ map-or-similar@^1.5.0: resolved "https://registry.npmjs.org/map-or-similar/-/map-or-similar-1.5.0.tgz#6de2653174adfb5d9edc33c69d3e92a1b76faf08" integrity sha1-beJlMXSt+12e3DPGnT6Sobdvrwg= +map-stream@~0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" + integrity sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ= + map-visit@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" @@ -12713,11 +12964,21 @@ minimist-options@^3.0.1: arrify "^1.0.1" is-plain-obj "^1.1.0" +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= + minimist@1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= +minimist@1.2.2: + version "1.2.2" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.2.tgz#b00a00230a1108c48c169e69a291aafda3aacd63" + integrity sha512-rIqbOrKb8GJmx/5bc2M0QchhUouMXSpd1RTclXsB41JdL+VtnojfaJR+h7F9k18/4kHUsBFgk80Uk+q569vjPA== + minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" @@ -12815,6 +13076,13 @@ mkdirp@*: resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.3.tgz#4cf2e30ad45959dddea53ad97d518b6c8205e1ea" integrity sha512-6uCP4Qc0sWsgMLy1EOqqS/3rjDHOEnsStVr/4vtAIK2Y5i2kA7lFFejYrpIyiN9w0pYf4ckeCYT9f1r1P9KX5g== +mkdirp@0.5.1: + version "0.5.1" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= + dependencies: + minimist "0.0.8" + mkdirp@0.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@~0.5.0, mkdirp@~0.5.1: version "0.5.3" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz#5a514b7179259287952881e94410ec5465659f8c" @@ -12827,7 +13095,7 @@ modify-values@^1.0.0: resolved "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== -moment@^2.24.0: +moment@2.24.0, moment@^2.24.0: version "2.24.0" resolved "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== @@ -13594,6 +13862,11 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0, once@~1.4.0: dependencies: wrappy "1" +onetime@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" + integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k= + onetime@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" @@ -13733,6 +14006,11 @@ osenv@^0.1.4, osenv@^0.1.5: os-homedir "^1.0.0" os-tmpdir "^1.0.0" +ospath@1.2.2: + version "1.2.2" + resolved "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" + integrity sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs= + p-defer@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" @@ -14164,6 +14442,13 @@ path-type@^4.0.0: resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +pause-stream@0.0.11: + version "0.0.11" + resolved "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" + integrity sha1-/lo0sMvOErWqaitAPuLnO2AvFEU= + dependencies: + through "~2.3" + pbkdf2@^3.0.3: version "3.0.17" resolved "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" @@ -14175,6 +14460,11 @@ pbkdf2@^3.0.3: safe-buffer "^5.0.1" sha.js "^2.4.8" +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= + performance-now@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" @@ -14185,7 +14475,7 @@ picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.0.7, picomatch@^2.2.1: resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== -pify@^2.0.0, pify@^2.3.0: +pify@^2.0.0, pify@^2.2.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= @@ -15004,7 +15294,7 @@ prettier@^1.18.2, prettier@^1.19.1: resolved "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== -pretty-bytes@^5.1.0: +pretty-bytes@5.3.0, pretty-bytes@^5.1.0: version "5.3.0" resolved "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.3.0.tgz#f2849e27db79fb4d6cfe24764fc4134f165989f2" integrity sha512-hjGrh+P926p4R4WbaB6OckyRtO0F0/lQBiT+0gnxjV+5kjPBrfVBFCsCLbMqVQeydvIoouYTCmmEURiH3R1Bdg== @@ -15184,6 +15474,13 @@ prr@~1.0.1: resolved "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= +ps-tree@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz#5e7425b89508736cdd4f2224d028f7bb3f722ebd" + integrity sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA== + dependencies: + event-stream "=3.3.4" + pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" @@ -15325,6 +15622,11 @@ raf@^3.4.1: dependencies: performance-now "^2.1.0" +ramda@0.26.1: + version "0.26.1" + resolved "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06" + integrity sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ== + ramda@^0.21.0: version "0.21.0" resolved "https://registry.npmjs.org/ramda/-/ramda-0.21.0.tgz#a001abedb3ff61077d4ff1d577d44de77e8d0a35" @@ -16183,6 +16485,13 @@ replace-in-file@^5.0.2: glob "^7.1.6" yargs "^15.0.2" +request-progress@3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe" + integrity sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4= + dependencies: + throttleit "^1.0.0" + request-promise-core@1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz#e9a3c081b51380dfea677336061fea879a829ee9" @@ -16190,7 +16499,7 @@ request-promise-core@1.1.3: dependencies: lodash "^4.17.15" -request-promise-native@^1.0.5, request-promise-native@^1.0.7: +request-promise-native@^1.0.5, request-promise-native@^1.0.7, request-promise-native@^1.0.8: version "1.0.8" resolved "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz#a455b960b826e44e2bf8999af64dff2bfe58cb36" integrity sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ== @@ -16225,6 +16534,31 @@ request@^2.85.0, request@^2.87.0, request@^2.88.0: tunnel-agent "^0.6.0" uuid "^3.3.2" +request@cypress-io/request#b5af0d1fa47eec97ba980cde90a13e69a2afcd16: + version "2.88.1" + resolved "https://codeload.github.com/cypress-io/request/tar.gz/b5af0d1fa47eec97ba980cde90a13e69a2afcd16" + 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" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -16339,6 +16673,14 @@ resolve@1.x, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.12.0, dependencies: path-parse "^1.0.6" +restore-cursor@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/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.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" @@ -16463,7 +16805,7 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.5.3: +rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.5.3, rxjs@^6.5.4: version "6.5.4" resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c" integrity sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q== @@ -17177,6 +17519,13 @@ split2@~1.0.0: dependencies: through2 "~2.0.0" +split@0.3: + version "0.3.3" + resolved "https://registry.npmjs.org/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" + integrity sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8= + dependencies: + through "2" + split@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" @@ -17258,6 +17607,19 @@ stacktrace-js@^2.0.0: stack-generator "^2.0.5" stacktrace-gps "^3.0.4" +start-server-and-test@^1.10.11: + version "1.10.11" + resolved "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-1.10.11.tgz#24290ee8a5ed15f4a34e9bb45a5d6ff93c93c83e" + integrity sha512-CZilaj293uQWdD4vgOxTOuzlCWxOyBm6bzmH1r6OGLG/q5zcBmGYevLfOimkg0kSn9jLHwYSXLuoKG/DDQJhww== + dependencies: + bluebird "3.7.2" + check-more-types "2.24.0" + debug "4.1.1" + execa "3.4.0" + lazy-ass "1.6.0" + ps-tree "1.2.0" + wait-on "4.0.0" + static-extend@^0.1.1: version "0.1.2" resolved "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" @@ -17297,6 +17659,13 @@ stream-combiner2@~1.1.1: duplexer2 "~0.1.0" readable-stream "^2.0.2" +stream-combiner@~0.0.4: + version "0.0.4" + resolved "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" + integrity sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ= + dependencies: + duplexer "~0.1.1" + stream-each@^1.1.0: version "1.2.3" resolved "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" @@ -17603,6 +17972,13 @@ stylis@3.5.0: resolved "https://registry.npmjs.org/stylis/-/stylis-3.5.0.tgz#016fa239663d77f868fef5b67cf201c4b7c701e1" integrity sha512-pP7yXN6dwMzAR29Q0mBrabPCe0/mNO1MSr93bhay+hcZondvMMTpeGyd8nbhYJdyperNT2DRxONQuUGcJr5iPw== +supports-color@7.1.0, supports-color@^7.0.0, supports-color@^7.1.0: + version "7.1.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" + integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== + dependencies: + has-flag "^4.0.0" + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" @@ -17622,13 +17998,6 @@ supports-color@^6.1.0: dependencies: has-flag "^3.0.0" -supports-color@^7.0.0, supports-color@^7.1.0: - version "7.1.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" - integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== - dependencies: - has-flag "^4.0.0" - supports-hyperlinks@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz#f663df252af5f37c5d49bbd7eeefa9e0b9e59e47" @@ -17870,6 +18239,11 @@ throttle-debounce@^2.1.0: resolved "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-2.1.0.tgz#257e648f0a56bd9e54fe0f132c4ab8611df4e1d5" integrity sha512-AOvyNahXQuU7NN+VVvOOX+uW6FPaWdAOdRP5HfwYxAfCzXTFKRMoIMk+n+po318+ktcChx+F1Dd91G3YHeMKyg== +throttleit@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" + integrity sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw= + through2@^2.0.0, through2@^2.0.2, through2@~2.0.0: version "2.0.5" resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" @@ -17885,7 +18259,7 @@ through2@^3.0.0: dependencies: readable-stream "2 || 3" -through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6: +through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@~2.3, through@~2.3.1: version "2.3.8" resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= @@ -17932,6 +18306,13 @@ tiny-warning@^1.0.0, tiny-warning@^1.0.2: resolved "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== +tmp@0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz#ee434a4e22543082e294ba6201dcc6eafefa2877" + integrity sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw== + dependencies: + rimraf "^2.6.3" + tmp@^0.0.33: version "0.0.33" resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -18367,6 +18748,11 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" +untildify@4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" + integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== + unzip-response@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" @@ -18439,7 +18825,7 @@ url-parse@^1.4.3: querystringify "^2.1.1" requires-port "^1.0.0" -url@^0.11.0: +url@0.11.0, url@^0.11.0: version "0.11.0" resolved "https://registry.npmjs.org/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= @@ -18608,6 +18994,18 @@ wait-for-expect@^3.0.2: resolved "https://registry.npmjs.org/wait-for-expect/-/wait-for-expect-3.0.2.tgz#d2f14b2f7b778c9b82144109c8fa89ceaadaa463" integrity sha512-cfS1+DZxuav1aBYbaO/kE06EOS8yRw7qOFoD3XtjTkYvCvh3zUvNST8DXK/nPaeqIzIv3P3kL3lRJn8iwOiSag== +wait-on@4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/wait-on/-/wait-on-4.0.0.tgz#4d7e4485ca759968897fd3b0cc50720c0b4ca959" + integrity sha512-QrW3J8LzS5ADPfD9Rx5S6KJck66xkqyiFKQs9jmUTkIhiEOmkzU7WRZc+MjsnmkrgjitS2xQ4bb13hnlQnKBUQ== + dependencies: + "@hapi/joi" "^16.1.8" + lodash "^4.17.15" + minimist "^1.2.0" + request "^2.88.0" + request-promise-native "^1.0.8" + rxjs "^6.5.4" + walker@^1.0.7, walker@~1.0.5: version "1.0.7" resolved "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" @@ -19382,6 +19780,21 @@ yargs@^8.0.2: y18n "^3.2.1" yargs-parser "^7.0.0" +yauzl@2.10.0: + version "2.10.0" + resolved "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + +yauzl@2.4.1: + version "2.4.1" + resolved "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005" + integrity sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU= + dependencies: + fd-slicer "~1.0.1" + yn@3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" From ec85cdd254f0f8e8d164ccb94f4546df2f775f21 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Fri, 27 Mar 2020 14:52:54 +0100 Subject: [PATCH 061/146] Add missing eslint plugin --- packages/app/package.json | 1 + yarn.lock | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/app/package.json b/packages/app/package.json index 9babc5f17b..8e66ab7b5e 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -51,6 +51,7 @@ "@testing-library/cypress": "^6.0.0", "@types/jquery": "^3.3.34", "cypress": "^4.2.0", + "eslint-plugin-cypress": "^2.10.3", "start-server-and-test": "^1.10.11" } } diff --git a/yarn.lock b/yarn.lock index 588f8e6956..dcde1de95a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7854,6 +7854,13 @@ eslint-module-utils@^2.4.1: debug "^2.6.9" pkg-dir "^2.0.0" +eslint-plugin-cypress@^2.10.3: + version "2.10.3" + resolved "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-2.10.3.tgz#82eba7e014954149d590402eecd0d4e147cc7f14" + integrity sha512-CvFeoCquShfO8gHNIKA1VpUTz78WtknMebLemBd1lRbcmJNjwpqCqpQYUG/XVja8GjdX/e2TJXYa+EUBxehtUg== + dependencies: + globals "^11.12.0" + eslint-plugin-flowtype@4.6.0: version "4.6.0" resolved "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-4.6.0.tgz#82b2bd6f21770e0e5deede0228e456cb35308451" @@ -9217,7 +9224,7 @@ global@^4.3.2, global@^4.4.0: min-document "^2.19.0" process "^0.11.10" -globals@^11.1.0: +globals@^11.1.0, globals@^11.12.0: version "11.12.0" resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== From 894d25b54b5d0c908003626c2a07d6391597e836 Mon Sep 17 00:00:00 2001 From: Vern Burton Date: Sat, 28 Mar 2020 18:40:28 -0500 Subject: [PATCH 062/146] updating Dockerfile to pull in custom default.conf to overload default file. --- Dockerfile | 1 + docker-compose.yml | 21 --------------------- docker/backstage.conf | 9 --------- docker/default.conf | 23 +++++++++++++++++++++++ 4 files changed, 24 insertions(+), 30 deletions(-) delete mode 100644 docker-compose.yml delete mode 100644 docker/backstage.conf create mode 100644 docker/default.conf diff --git a/Dockerfile b/Dockerfile index 39d51a35f2..5337207235 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,3 +15,4 @@ RUN yarn build FROM nginx:mainline COPY --from=builder /app/packages/app/build /usr/share/nginx/html +COPY ./docker/default.conf /etc/nginx/conf.d/default.conf diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 90c2e6d395..0000000000 --- a/docker-compose.yml +++ /dev/null @@ -1,21 +0,0 @@ -version: '3.7' -services: - app: - volumes: - - app_dir:/app/packages/app/build - build: . - - web: - ports: - - 8080:80 - volumes: - - ./docker/backstage.conf:/etc/nginx/conf.d/backstage.conf - - type: volume - source: app_dir - target: /usr/share/backstage/html - volume: - nocopy: true - image: nginx:mainline - -volumes: - app_dir: \ No newline at end of file diff --git a/docker/backstage.conf b/docker/backstage.conf deleted file mode 100644 index b309ab0b01..0000000000 --- a/docker/backstage.conf +++ /dev/null @@ -1,9 +0,0 @@ -server { - listen 80 default_server; - root /usr/share/backstage/html; - server_name backstage.local; - index index.html index.htm; - location / { - try_files $uri /index.html; - } -} \ No newline at end of file diff --git a/docker/default.conf b/docker/default.conf new file mode 100644 index 0000000000..d01be24dc7 --- /dev/null +++ b/docker/default.conf @@ -0,0 +1,23 @@ +server { + listen 80; + server_name localhost; + + #charset koi8-r; + #access_log /var/log/nginx/host.access.log main; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri /index.html; + } + + #error_page 404 /404.html; + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} + From b1a2eef8fd35d03b0177b74a57d488d087c6090e Mon Sep 17 00:00:00 2001 From: Mateus Marquezini Date: Sun, 29 Mar 2020 12:11:07 -0300 Subject: [PATCH 063/146] #400 Adding Information Card component to Storybook --- .../core/src/layout/InfoCard/BottomLink.js | 23 ++++++++-- packages/core/src/layout/InfoCard/InfoCard.js | 23 ++++++---- .../src/layout/InfoCard/InfoCard.stories.tsx | 42 +++++++++++++++++++ packages/storybook/.storybook/main.js | 28 +++++++++---- 4 files changed, 96 insertions(+), 20 deletions(-) create mode 100644 packages/core/src/layout/InfoCard/InfoCard.stories.tsx diff --git a/packages/core/src/layout/InfoCard/BottomLink.js b/packages/core/src/layout/InfoCard/BottomLink.js index 786fe59607..c3d7f1efc0 100644 --- a/packages/core/src/layout/InfoCard/BottomLink.js +++ b/packages/core/src/layout/InfoCard/BottomLink.js @@ -17,9 +17,24 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { Link } from '@material-ui/core'; -import { Divider, ListItemText } from '@material-ui/core'; +import { Divider, ListItemText, withStyles } from '@material-ui/core'; import { ListItem, ListItemIcon } from '@material-ui/core'; import ArrowIcon from '@material-ui/icons/ArrowForward'; +import grey from '@material-ui/core/colors/grey'; + +const LinkBottomText = withStyles({ + primary: { + fontWeight: '700', + color: grey[900], + }, +})(ListItemText); + +const ListItemBottom = withStyles({ + root: { + maxWidth: 'fit-content', + padding: '16px 16px 16px 20px', + }, +})(ListItem); export default class BottomLink extends Component { static propTypes = { @@ -34,12 +49,12 @@ export default class BottomLink extends Component {
- + + {title} - {title} - +
); diff --git a/packages/core/src/layout/InfoCard/InfoCard.js b/packages/core/src/layout/InfoCard/InfoCard.js index 6e771c8200..0b2abfb164 100644 --- a/packages/core/src/layout/InfoCard/InfoCard.js +++ b/packages/core/src/layout/InfoCard/InfoCard.js @@ -29,7 +29,13 @@ import BottomLink from './BottomLink'; import textContent from 'react-addons-text-content'; -const BoldHeader = withStyles({ title: { fontWeight: '700' } })(CardHeader); +const BoldHeader = withStyles({ + title: { fontWeight: '700' }, + subheader: { paddingTop: '2px' }, + root: { + padding: '16px 16px 16px 20px', + }, +})(CardHeader); const CardActionsTopRight = withStyles({ root: { display: 'inline-block', @@ -183,12 +189,15 @@ class InfoCard extends Component { > {title && ( - + <> + + + )} {actionsTopRight && ( {actionsTopRight} diff --git a/packages/core/src/layout/InfoCard/InfoCard.stories.tsx b/packages/core/src/layout/InfoCard/InfoCard.stories.tsx new file mode 100644 index 0000000000..945f233e16 --- /dev/null +++ b/packages/core/src/layout/InfoCard/InfoCard.stories.tsx @@ -0,0 +1,42 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import InfoCard from '.'; + +const linkInfo = { title: 'Go to XYZ Location', link: '#' }; + +export default { + title: 'Information Card', + component: InfoCard, +}; + +export const Default = () => ( + +
+ +); + +export const Subhead = () => ( + +
+ +); + +export const LinkInFooter = () => ( + +
+ +); diff --git a/packages/storybook/.storybook/main.js b/packages/storybook/.storybook/main.js index 7a51d41e0d..afed6723d8 100644 --- a/packages/storybook/.storybook/main.js +++ b/packages/storybook/.storybook/main.js @@ -5,17 +5,27 @@ module.exports = { ], addons: ['@storybook/addon-actions', '@storybook/addon-links'], webpackFinal: async config => { - config.module.rules.push({ - test: /\.(ts|tsx)$/, - use: [ - { - loader: require.resolve('ts-loader'), - options: { - transpileOnly: true, + config.module.rules.push( + { + test: /\.(ts|tsx)$/, + use: [ + { + loader: require.resolve('ts-loader'), + options: { + transpileOnly: true, + }, }, + ], + }, + { + test: /\.(js|jsx)$/, + loader: 'babel-loader', + options: { + presets: ['@babel/preset-react'], + plugins: ['@babel/plugin-proposal-class-properties'], }, - ], - }); + }, + ); config.resolve.extensions.push('.ts', '.tsx'); return config; }, From 3ce675857ed03ccffe173350ad4336124995e2e1 Mon Sep 17 00:00:00 2001 From: Mateus Marquezini Date: Sun, 29 Mar 2020 12:40:14 -0300 Subject: [PATCH 064/146] #400 fixed spread usage --- packages/core/src/layout/InfoCard/InfoCard.stories.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/core/src/layout/InfoCard/InfoCard.stories.tsx b/packages/core/src/layout/InfoCard/InfoCard.stories.tsx index 945f233e16..47edbde229 100644 --- a/packages/core/src/layout/InfoCard/InfoCard.stories.tsx +++ b/packages/core/src/layout/InfoCard/InfoCard.stories.tsx @@ -16,6 +16,7 @@ import React from 'react'; import InfoCard from '.'; +const cardContentStyle = { height: '200px' }; const linkInfo = { title: 'Go to XYZ Location', link: '#' }; export default { @@ -25,18 +26,18 @@ export default { export const Default = () => ( -
+
); export const Subhead = () => ( -
+
); export const LinkInFooter = () => ( - -
+ +
); From 9b7a8712fe721795612a888260b9da2330f97b86 Mon Sep 17 00:00:00 2001 From: Szymon Graczyk Date: Sun, 29 Mar 2020 21:06:35 +0200 Subject: [PATCH 065/146] Rewrite layout/Header to use TypeScript and hooks * use makeStyles hook generator * write fragments as components * use types instead of PropTypes --- packages/core/src/layout/Header/Header.js | 157 -------------- .../{Header.test.js => Header.test.tsx} | 12 +- packages/core/src/layout/Header/Header.tsx | 197 ++++++++++++++++++ packages/core/src/theme/theme.d.ts | 7 + 4 files changed, 213 insertions(+), 160 deletions(-) delete mode 100644 packages/core/src/layout/Header/Header.js rename packages/core/src/layout/Header/{Header.test.js => Header.test.tsx} (82%) create mode 100644 packages/core/src/layout/Header/Header.tsx diff --git a/packages/core/src/layout/Header/Header.js b/packages/core/src/layout/Header/Header.js deleted file mode 100644 index f58dce21f7..0000000000 --- a/packages/core/src/layout/Header/Header.js +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React, { Component, Fragment } from 'react'; -import PropTypes from 'prop-types'; -import { Typography, withStyles, Tooltip } from '@material-ui/core'; -import { Theme } from '../Page/Page'; -// import { Link } from 'shared/components'; -import Waves from './Waves'; -import Helmet from 'react-helmet'; - -class Header extends Component { - static propTypes = { - type: PropTypes.string, - typeLink: PropTypes.string, - title: PropTypes.node.isRequired, - tooltip: PropTypes.string, - subtitle: PropTypes.node, - pageTitleOverride: PropTypes.string, - style: PropTypes.object, - component: PropTypes.object, - }; - - typeFragment() { - const { type, typeLink, classes } = this.props; - if (!type) { - return null; - } - - return typeLink ? ( - // - {type} - ) : ( - // - {type} - ); - } - - titleFragment() { - const { title, pageTitleOverride, classes, tooltip } = this.props; - const FinalTitle = ( - - {title || pageTitleOverride} - - ); - if (tooltip) { - return ( - - {FinalTitle} - - ); - } - return FinalTitle; - } - - subtitleFragment() { - const { subtitle, classes } = this.props; - if (!subtitle) { - return null; - } else if (typeof subtitle !== 'string') { - return subtitle; - } - - return ( - - {subtitle} - - ); - } - - render() { - const { title, pageTitleOverride, children, style, classes } = this.props; - const pageTitle = pageTitleOverride || title; - return ( - - - - {theme => ( -
- -
- {this.typeFragment()} - {this.titleFragment()} - {this.subtitleFragment()} -
-
{children}
-
- )} -
-
- ); - } -} - -const styles = theme => ({ - header: { - gridArea: 'pageHeader', - padding: theme.spacing(3), - minHeight: 118, - width: '100%', - boxShadow: '0 0 8px 3px rgba(20, 20, 20, 0.3)', - position: 'relative', - zIndex: 100, - display: 'flex', - flexDirection: 'row', - flexWrap: 'wrap', - justifyContent: 'flex-end', - alignItems: 'center', - }, - leftItemsBox: { - flex: '1 1 auto', - }, - rightItemsBox: { - flex: '0 1 auto', - display: 'flex', - flexDirection: 'row', - flexWrap: 'wrap', - alignItems: 'center', - marginRight: theme.spacing(6), - }, - title: { - color: theme.palette.bursts.fontColor, - lineHeight: '1.0em', - wordBreak: 'break-all', - fontSize: 'calc(24px + 6 * ((100vw - 320px) / 680))', - marginBottom: theme.spacing(1), - }, - subtitle: { - color: 'rgba(255, 255, 255, 0.8)', - lineHeight: '1.0em', - }, - type: { - textTransform: 'uppercase', - fontSize: 9, - opacity: 0.8, - marginBottom: 10, - color: theme.palette.bursts.fontColor, - }, -}); - -export default withStyles(styles)(Header); diff --git a/packages/core/src/layout/Header/Header.test.js b/packages/core/src/layout/Header/Header.test.tsx similarity index 82% rename from packages/core/src/layout/Header/Header.test.js rename to packages/core/src/layout/Header/Header.test.tsx index 0c30f82aee..5213fe1ac3 100644 --- a/packages/core/src/layout/Header/Header.test.js +++ b/packages/core/src/layout/Header/Header.test.tsx @@ -36,18 +36,24 @@ describe('
', () => { }); it('should override document title', () => { - const rendered = render(wrapInThemedTestApp(
)); + const rendered = render( + wrapInThemedTestApp(
), + ); rendered.getByText('Title1'); rendered.getByText('defaultTitle: Title2 | Backstage'); }); it('should have subtitle', () => { - const rendered = render(wrapInThemedTestApp(
)); + const rendered = render( + wrapInThemedTestApp(
), + ); rendered.getByText('Subtitle'); }); it('should have type rendered', () => { - const rendered = render(wrapInThemedTestApp(
)); + const rendered = render( + wrapInThemedTestApp(
), + ); rendered.getByText('tool'); }); }); diff --git a/packages/core/src/layout/Header/Header.tsx b/packages/core/src/layout/Header/Header.tsx new file mode 100644 index 0000000000..5b3bb7ee60 --- /dev/null +++ b/packages/core/src/layout/Header/Header.tsx @@ -0,0 +1,197 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { Fragment, ReactNode, CSSProperties, FC } from 'react'; +import Helmet from 'react-helmet'; +import { Typography, Tooltip, makeStyles } from '@material-ui/core'; +import { Theme } from '../Page/Page'; +// import { Link } from 'shared/components'; +import { BackstageTheme } from '../../theme/theme'; +import Waves from './Waves'; + +const useStyles = makeStyles(theme => ({ + header: { + gridArea: 'pageHeader', + padding: theme.spacing(3), + minHeight: 118, + width: '100%', + boxShadow: '0 0 8px 3px rgba(20, 20, 20, 0.3)', + position: 'relative', + zIndex: 100, + display: 'flex', + flexDirection: 'row', + flexWrap: 'wrap', + justifyContent: 'flex-end', + alignItems: 'center', + }, + leftItemsBox: { + flex: '1 1 auto', + }, + rightItemsBox: { + flex: '0 1 auto', + display: 'flex', + flexDirection: 'row', + flexWrap: 'wrap', + alignItems: 'center', + marginRight: theme.spacing(6), + }, + title: { + color: theme.palette.bursts.fontColor, + lineHeight: '1.0em', + wordBreak: 'break-all', + fontSize: 'calc(24px + 6 * ((100vw - 320px) / 680))', + marginBottom: theme.spacing(1), + }, + subtitle: { + color: 'rgba(255, 255, 255, 0.8)', + lineHeight: '1.0em', + }, + type: { + textTransform: 'uppercase', + fontSize: 9, + opacity: 0.8, + marginBottom: 10, + color: theme.palette.bursts.fontColor, + }, +})); + +type HeaderStyles = ReturnType; + +type Props = { + component?: ReactNode; + pageTitleOverride?: string; + style?: CSSProperties; + subtitle?: ReactNode; + title: ReactNode; + tooltip?: string; + type?: string; + typeLink?: string; +}; + +type TypeFragmentProps = { + classes: HeaderStyles; + type?: Props['title']; + typeLink?: Props['typeLink']; +}; + +type TitleFragmentProps = { + classes: HeaderStyles; + pageTitle: string | ReactNode; + tooltip?: Props['tooltip']; +}; + +type SubtitleFragmentProps = { + classes: HeaderStyles; + subtitle?: Props['subtitle']; +}; + +const TypeFragment: FC = ({ type, typeLink, classes }) => { + if (!type) { + return null; + } + + if (!typeLink) { + return ( + // + {type} + ); + } + + return ( + // + {type} + ); +}; + +const TitleFragment: FC = ({ + pageTitle, + classes, + tooltip, +}) => { + const FinalTitle = ( + + {pageTitle} + + ); + + if (!tooltip) { + return FinalTitle; + } + + return ( + + {FinalTitle} + + ); +}; + +const SubtitleFragment: FC = ({ classes, subtitle }) => { + if (!subtitle) { + return null; + } + + if (typeof subtitle !== 'string') { + return <>{subtitle}; + } + + return ( + + {subtitle} + + ); +}; + +export const Header: FC = ({ + children, + pageTitleOverride, + style, + subtitle, + title, + tooltip, + type, + typeLink, +}) => { + const classes = useStyles(); + const documentTitle = pageTitleOverride || title; + const pageTitle = title || pageTitleOverride; + const titleTemplate = `${documentTitle} | %s | Backstage`; + const defaultTitle = `${documentTitle} | Backstage`; + + return ( + + + + {theme => ( +
+ +
+ + + +
+
{children}
+
+ )} +
+
+ ); +}; + +export default Header; diff --git a/packages/core/src/theme/theme.d.ts b/packages/core/src/theme/theme.d.ts index db4dfe5e86..7c9f552310 100644 --- a/packages/core/src/theme/theme.d.ts +++ b/packages/core/src/theme/theme.d.ts @@ -39,5 +39,12 @@ export type BackstageTheme = Theme & { linkHover: string; link: string; gold: string; + bursts: { + fontColor: string; + slackChannelText: string; + backgroundColor: { + default: string; + }; + }; }; }; From 9235807f1407901b65829a0fc1e2a69c5267592e Mon Sep 17 00:00:00 2001 From: Ahmet Soormally Date: Fri, 27 Mar 2020 18:47:46 +0000 Subject: [PATCH 066/146] document how to override the default port Port 3000 is a common port in the development environment. This documents how you can start backstage with a different port to the default. Moved section for development environment to own page within docs in order to keep README small. --- README.md | 12 ++----- docs/getting-started/README.md | 1 + .../development-environment.md | 35 +++++++++++++++++++ 3 files changed, 38 insertions(+), 10 deletions(-) create mode 100644 docs/getting-started/development-environment.md diff --git a/README.md b/README.md index 1c20ec8cc6..2145e62fa8 100644 --- a/README.md +++ b/README.md @@ -69,16 +69,8 @@ $ yarn start The final `yarn start` command should open a local instance of Backstage in your browser, otherwise open one of the URLs printed in the terminal. -### (Optional)Try on Docker - -Run the following commands if you have Docker environment - -```bash -$ docker build . -t spotify/backstage -$ docker run --rm -it -p 80:80 spotify/backstage -``` - -Then open http://localhost/ on your browser. +For more complex development environment configuration, see the +[Development Environment](docs/getting-started/development-environment.md) section of the Getting Started docs. ## Documentation diff --git a/docs/getting-started/README.md b/docs/getting-started/README.md index c37e61e82f..719e946a7e 100644 --- a/docs/getting-started/README.md +++ b/docs/getting-started/README.md @@ -2,6 +2,7 @@ Here is a collection of tutorials that will guide you through setting up and extending an instance of Backstage with your own plugins. +- [Development Environment](development-environment.md) - [Create a Backstage plugin](create-a-plugin.md) - [Structure of a plugin](structure-of-a-plugin.md) - Using Backstage components (TODO) diff --git a/docs/getting-started/development-environment.md b/docs/getting-started/development-environment.md new file mode 100644 index 0000000000..e836a0c5d2 --- /dev/null +++ b/docs/getting-started/development-environment.md @@ -0,0 +1,35 @@ +# Development Environment + +Open a terminal window and start the web app using the following commands from the project root: + +```bash +$ yarn install # may take a while + +$ yarn start +``` + +The final `yarn start` command should open a local instance of Backstage in your browser, otherwise open one of the URLs printed in the terminal. + +By default, backstage will start on port 3000, however you can override this by setting an environment variable `PORT` on your local machine. e.g. `export PORT=8080` then running `yarn start`. Or `PORT=8080 yarn start`. + +Once successfully started, you should see the following message in your terminal window: + +``` +You can now view @backstage/app in the browser. + + Local: http://localhost:8080 + On Your Network: http://192.168.1.224:8080 +``` + +### (Optional)Try on Docker + +Run the following commands if you have Docker environment + +```bash +$ docker build . -t spotify/backstage +$ docker run --rm -it -p 80:80 spotify/backstage +``` + +Then open http://localhost/ on your browser. + +[Back to Docs](README.md) From 93bbbcd5ee5baa38298904840ebbf53b69bb8829 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 30 Mar 2020 11:07:56 +0200 Subject: [PATCH 067/146] github/workflows: move storybook deploy to separate workflow --- .github/workflows/frontend.yml | 27 ++------------ .github/workflows/storybook-deploy.yml | 50 ++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 25 deletions(-) create mode 100644 .github/workflows/storybook-deploy.yml diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index d972ae7c90..22fc24df23 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -3,11 +3,6 @@ name: Frontend CI on: pull_request: types: [opened, reopened, edited, synchronize] - push: - branches: - - master - paths: - - 'packages/core/src/**' jobs: build: @@ -47,23 +42,5 @@ jobs: - run: yarn lint - run: yarn build - run: yarn test - - deploy-storybook: - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [12.x] - steps: - - uses: actions/checkout@v2 - - name: build storybook - run: | - yarn install - yarn workspace storybook build-storybook - - name: deploy gh-pages - uses: JamesIves/github-pages-deploy-action@3.4.2 - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - BRANCH: gh-pages - FOLDER: packages/storybook/dist + - name: verify storybook + run: yarn workspace storybook build-storybook diff --git a/.github/workflows/storybook-deploy.yml b/.github/workflows/storybook-deploy.yml new file mode 100644 index 0000000000..9a266144bc --- /dev/null +++ b/.github/workflows/storybook-deploy.yml @@ -0,0 +1,50 @@ +name: Deploy Storybook + +on: + push: + branches: + - master + paths: + - '.github/workflows/storybook-deploy.yml' + - 'packages/storybook/**' + - 'packages/core/src/**' + +jobs: + deploy-storybook: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [12.x] + steps: + - uses: actions/checkout@v2 + - name: find location of global yarn cache + id: yarn-cache + run: echo "::set-output name=dir::$(yarn cache dir)" + - name: cache global yarn cache + uses: actions/cache@v1 + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + - name: cache node_modules + uses: actions/cache@v1 + with: + path: node_modules + key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} + + - name: use node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + registry-url: https://registry.npmjs.org/ # Needed for auth + - run: yarn install + - name: build storybook + run: yarn workspace storybook build-storybook + - name: deploy storybook to gh-pages + uses: JamesIves/github-pages-deploy-action@3.4.2 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BRANCH: gh-pages + FOLDER: packages/storybook/dist From 8145ba8b945f358b5dec62e6dad3cc49741a99aa Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Thu, 26 Mar 2020 11:21:46 +0100 Subject: [PATCH 068/146] [docs] added skeleton --- docs/reference/README.md | 1 + docs/reference/createPlugin-feature-flags.md | 15 +++++++++++++++ docs/reference/createPlugin.md | 9 ++++++--- 3 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 docs/reference/createPlugin-feature-flags.md diff --git a/docs/reference/README.md b/docs/reference/README.md index 8438917945..dd926d57dc 100644 --- a/docs/reference/README.md +++ b/docs/reference/README.md @@ -4,5 +4,6 @@ APIs and Components - [createPlugin](createPlugin.md) - [createPlugin - router](createPlugin-router.md) +- [createPlugin - feature flags](createPlugin-feature-flags.md) [Back to Docs](../README.md) diff --git a/docs/reference/createPlugin-feature-flags.md b/docs/reference/createPlugin-feature-flags.md new file mode 100644 index 0000000000..d8ad043ddc --- /dev/null +++ b/docs/reference/createPlugin-feature-flags.md @@ -0,0 +1,15 @@ +# createPlugin - feature flags + +The `featureFlags` object passed to the `register` function makes it possible for plugins to register Feature Flags in Backstage for users to opt into. You can use this to split out logic in your code for manual A/B testing, etc. + +```typescript +// This needs to be updated. +``` + +Then, later, if you want to check if the user has enabled them: + +```typescript +// This needs to be updated. +``` + +[Back to References](README.md) diff --git a/docs/reference/createPlugin.md b/docs/reference/createPlugin.md index 77048199ef..c0b4dece71 100644 --- a/docs/reference/createPlugin.md +++ b/docs/reference/createPlugin.md @@ -17,13 +17,14 @@ type PluginHooks = { }; ``` -[Read more about the router here](createPlugin-router.md) +- [Read more about the router here](createPlugin-router.md) +- [Read more about feature flags here](createPlugin-feature-flags.md) ## Example Uses ### Creating a basic plugin -Showcasing adding multiple routes and a redirect. +Showcasing adding multiple routes, a feature flag and a redirect. ```jsx import { createPlugin } from '@backstage/core'; @@ -31,7 +32,9 @@ import ExampleComponent from './components/ExampleComponent'; export default createPlugin({ id: 'new-plugin', - register({ router }) { + register({ router, featureFlags: { registerFeatureFlag } }) { + registerFeatureFlag('enable-example-component'); + router.registerRoute('/new-plugin', ExampleComponent); }, }); From 45d7e2736f25fae55a1f32e06040ebf2d5914f5f Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Thu, 26 Mar 2020 11:34:30 +0100 Subject: [PATCH 069/146] [core/api/plugins] added featureFlags.registerFeatureFlag --- packages/core/src/api/plugin/Plugin.tsx | 17 ++++++++++++++++- packages/core/src/api/plugin/types.ts | 13 ++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/packages/core/src/api/plugin/Plugin.tsx b/packages/core/src/api/plugin/Plugin.tsx index efaabd0ff1..aa1ff417b7 100644 --- a/packages/core/src/api/plugin/Plugin.tsx +++ b/packages/core/src/api/plugin/Plugin.tsx @@ -15,7 +15,12 @@ */ import { ComponentType } from 'react'; -import { PluginOutput, RoutePath, RouteOptions } from './types'; +import { + PluginOutput, + RoutePath, + RouteOptions, + FeatureFlagName, +} from './types'; import { Widget } from '../widgetView/types'; export type PluginConfig = { @@ -26,6 +31,7 @@ export type PluginConfig = { export type PluginHooks = { router: RouterHooks; widgets: WidgetHooks; + featureFlags: FeatureFlagsHooks; }; export type RouterHooks = { @@ -46,6 +52,10 @@ export type WidgetHooks = { add(widget: Widget): void; }; +export type FeatureFlagsHooks = { + registerFeatureFlag(name: FeatureFlagName): void; +}; + export const registerSymbol = Symbol('plugin-register'); export const outputSymbol = Symbol('plugin-output'); @@ -78,6 +88,11 @@ export default class Plugin { outputs.push({ type: 'widget', widget }); }, }, + featureFlags: { + registerFeatureFlag(name) { + outputs.push({ type: 'feature-flag', name }); + }, + }, }); this.storedOutput = outputs; diff --git a/packages/core/src/api/plugin/types.ts b/packages/core/src/api/plugin/types.ts index 47c121fae8..b006e06d9a 100644 --- a/packages/core/src/api/plugin/types.ts +++ b/packages/core/src/api/plugin/types.ts @@ -43,4 +43,15 @@ export type WidgetOutput = { widget: Widget; }; -export type PluginOutput = RouteOutput | RedirectRouteOutput | WidgetOutput; +export type FeatureFlagName = string; + +export type FeatureFlagOutput = { + type: 'feature-flag'; + name: FeatureFlagName; +}; + +export type PluginOutput = + | RouteOutput + | RedirectRouteOutput + | WidgetOutput + | FeatureFlagOutput; From e6a88a5dc0feadc99b2f37bdcb46ae140ff7353f Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Thu, 26 Mar 2020 11:56:37 +0100 Subject: [PATCH 070/146] [core/AppBuilder] collect list of registered feature flags --- packages/core/src/api/app/AppBuilder.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/core/src/api/app/AppBuilder.tsx b/packages/core/src/api/app/AppBuilder.tsx index ab6f60a293..b29cef79c3 100644 --- a/packages/core/src/api/app/AppBuilder.tsx +++ b/packages/core/src/api/app/AppBuilder.tsx @@ -19,6 +19,7 @@ import { Route, Switch, Redirect } from 'react-router-dom'; import { AppContextProvider } from './AppContext'; import { App } from './types'; import BackstagePlugin from '../plugin/Plugin'; +import { FeatureFlagName } from '../plugin/Plugin/types'; import { IconComponent, SystemIcons, @@ -62,6 +63,7 @@ export default class AppBuilder { const app = new AppImpl(this.systemIcons); const routes = new Array(); + const registeredFeatureFlags = new Array<{ name: FeatureFlagName }>(); for (const plugin of this.plugins.values()) { for (const output of plugin.output()) { @@ -87,6 +89,10 @@ export default class AppBuilder { ); break; } + case 'feature-flag': { + registeredFeatureFlags.push({ name: output.name }); + break; + } default: break; } From 79654e477e47dfae70fdefee0e0d9e6c759917a6 Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Thu, 26 Mar 2020 12:18:05 +0100 Subject: [PATCH 071/146] [core/apis/definitions] added featureFlags definition --- .../src/api/apis/definitions/featureFlags.ts | 53 +++++++++++++++++++ .../core/src/api/apis/definitions/index.ts | 1 + 2 files changed, 54 insertions(+) create mode 100644 packages/core/src/api/apis/definitions/featureFlags.ts diff --git a/packages/core/src/api/apis/definitions/featureFlags.ts b/packages/core/src/api/apis/definitions/featureFlags.ts new file mode 100644 index 0000000000..c3656ac4cd --- /dev/null +++ b/packages/core/src/api/apis/definitions/featureFlags.ts @@ -0,0 +1,53 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import ApiRef from '../ApiRef'; +import { FeatureFlagName } from '../../plugin/types'; + +/** + * The feature flags API is used to toggle functionality to users across plugins and Backstage. + * + * Plugins can use this API to register feature flags that they have available + * for users to enable/disable, and this API will centralize the current user's + * state of which feature flags they would like to enable. + * + * This is ideal for Backstage plugins, as well as your own App, to trial incomplete + * or unstable upcoming features. Although there will be a common interface for users + * to enable and disable feature flags, this API acts as another way to enable/disable. + */ +export type FeatureFlagsApi = { + /** + * Get the current user's status of a Feature Flag + * + * @returns bool True if the current user has enabled the feature flag + */ + getItem(name: FeatureFlagName): boolean; + + /** + * Enable an registered feature flag. + */ + enable(name: FeatureFlagName): void; + + /** + * Disable an registered feature flag. + */ + disable(name: FeatureFlagName): void; +}; + +export const featureFlagsApiRef = new ApiRef({ + id: 'core.featureflags', + description: 'Used to toggle functionality in features across Backstage', +}); diff --git a/packages/core/src/api/apis/definitions/index.ts b/packages/core/src/api/apis/definitions/index.ts index bdf2a0b6a2..55923b50b7 100644 --- a/packages/core/src/api/apis/definitions/index.ts +++ b/packages/core/src/api/apis/definitions/index.ts @@ -21,3 +21,4 @@ // If you think some API definition is missing, please open an Issue or send a PR! export * from './error'; +export * from './featureFlags'; From 817c4760504a7568685ea7436b565cfde0cd1989 Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Thu, 26 Mar 2020 12:22:01 +0100 Subject: [PATCH 072/146] [docs] updated with typescript definitions + code sample --- docs/reference/createPlugin-feature-flags.md | 24 +++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/docs/reference/createPlugin-feature-flags.md b/docs/reference/createPlugin-feature-flags.md index d8ad043ddc..6c3a5f0bc5 100644 --- a/docs/reference/createPlugin-feature-flags.md +++ b/docs/reference/createPlugin-feature-flags.md @@ -3,13 +3,31 @@ The `featureFlags` object passed to the `register` function makes it possible for plugins to register Feature Flags in Backstage for users to opt into. You can use this to split out logic in your code for manual A/B testing, etc. ```typescript -// This needs to be updated. +export type FeatureFlagsHooks = { + registerFeatureFlag(name: FeatureFlagName): void; +}; ``` Then, later, if you want to check if the user has enabled them: -```typescript -// This needs to be updated. +```tsx +import React, { FC } from 'react'; +import { Button } from '@material-ui/core'; +import { featureFlagsApiRef, useApi } from '@backstage/core'; + +const ExampleButton: FC<{}> = () => { + const featureFlagsApi = useApi(featureFlagsApiRef); + + const handleClick = () => { + featureFlagsApi.enable('enable-example-feature'); + }; + + return ( + + ); +}; ``` [Back to References](README.md) From 3e6d2ce730d2ef02e57adc0e11b57cf9b1f86f53 Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Thu, 26 Mar 2020 12:56:56 +0100 Subject: [PATCH 073/146] [core/api/app] add FeatureFlags API implementation --- packages/core/src/api/app/FeatureFlags.tsx | 73 ++++++++++++++++++++++ packages/core/src/api/index.ts | 1 + 2 files changed, 74 insertions(+) create mode 100644 packages/core/src/api/app/FeatureFlags.tsx diff --git a/packages/core/src/api/app/FeatureFlags.tsx b/packages/core/src/api/app/FeatureFlags.tsx new file mode 100644 index 0000000000..31a3c37341 --- /dev/null +++ b/packages/core/src/api/app/FeatureFlags.tsx @@ -0,0 +1,73 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// import React, { createContext, useContext, useState, FC } from 'react'; +import { FeatureFlagName } from '../plugin/Plugin/types'; +import { FeatureFlagsApi } from '../apis/definitions/featureFlags'; + +// TODO: figure out where to put implementations of APIs, both inside apps +// but also in core/separate package. +export class FeatureFlags implements FeatureFlagsApi { + private readonly localStorageKey = 'featureFlags'; + + private getEnabledFeatureFlags(): Set { + if (!('localStorage' in window)) { + throw new Error( + 'Feature Flags are not supported on browsers without the Local Storage API', + ); + } + + try { + const featureFlagsJson = window.localStorage.getItem( + this.localStorageKey, + ); + return new Set<>(Object.keys(JSON.parse(featureFlagsJson!))); + } catch (err) { + return new Set<>(); + } + } + + private saveFeatureFlags(flags: Set): void { + if (!('localStorage' in window)) { + throw new Error( + 'Feature Flags are not supported on browsers without the Local Storage API', + ); + } + + window.localStorage.setItem( + this.localStorageKey, + JSON.stringify( + [...flags].reduce((list, flag) => ({ ...list, [flag]: true }), {}), + ), + ); + } + + getItem(name: FeatureFlagName): boolean { + return this.getFeatureFlags().has(name); + } + + enable(name: FeatureFlagName): void { + const flags = this.getFeatureFlags(); + flags.add(name); + this.saveFeatureFlags(flags); + } + + disable(name: FeatureFlagName): void { + const flags = this.getFeatureFlags(); + flags.delete(name); + this.saveFeatureFlags(flags); + } +} diff --git a/packages/core/src/api/index.ts b/packages/core/src/api/index.ts index dff75514ae..e60a7efa18 100644 --- a/packages/core/src/api/index.ts +++ b/packages/core/src/api/index.ts @@ -16,4 +16,5 @@ export * from './api'; export * from './apis'; +export { FeatureFlags } from './app/FeatureFlags'; export { useApp } from './app/AppContext'; From 10a7102f4c48604597db02f4b0f1f6c9ace3f7e8 Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Thu, 26 Mar 2020 13:12:17 +0100 Subject: [PATCH 074/146] [core/api/app] Set FeatureFlags class to static --- packages/core/src/api/app/FeatureFlags.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/core/src/api/app/FeatureFlags.tsx b/packages/core/src/api/app/FeatureFlags.tsx index 31a3c37341..c75e072149 100644 --- a/packages/core/src/api/app/FeatureFlags.tsx +++ b/packages/core/src/api/app/FeatureFlags.tsx @@ -21,9 +21,9 @@ import { FeatureFlagsApi } from '../apis/definitions/featureFlags'; // TODO: figure out where to put implementations of APIs, both inside apps // but also in core/separate package. export class FeatureFlags implements FeatureFlagsApi { - private readonly localStorageKey = 'featureFlags'; + private static readonly localStorageKey = 'featureFlags'; - private getEnabledFeatureFlags(): Set { + private static getEnabledFeatureFlags(): Set { if (!('localStorage' in window)) { throw new Error( 'Feature Flags are not supported on browsers without the Local Storage API', @@ -40,7 +40,7 @@ export class FeatureFlags implements FeatureFlagsApi { } } - private saveFeatureFlags(flags: Set): void { + private static saveFeatureFlags(flags: Set): void { if (!('localStorage' in window)) { throw new Error( 'Feature Flags are not supported on browsers without the Local Storage API', @@ -55,17 +55,17 @@ export class FeatureFlags implements FeatureFlagsApi { ); } - getItem(name: FeatureFlagName): boolean { + static getItem(name: FeatureFlagName): boolean { return this.getFeatureFlags().has(name); } - enable(name: FeatureFlagName): void { + static enable(name: FeatureFlagName): void { const flags = this.getFeatureFlags(); flags.add(name); this.saveFeatureFlags(flags); } - disable(name: FeatureFlagName): void { + static disable(name: FeatureFlagName): void { const flags = this.getFeatureFlags(); flags.delete(name); this.saveFeatureFlags(flags); From b865bdd4729d8b433905ebc21aa6198067dd16ae Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Thu, 26 Mar 2020 13:35:45 +0100 Subject: [PATCH 075/146] [core] fix bad imports --- packages/core/src/api/app/AppBuilder.tsx | 2 +- packages/core/src/api/app/FeatureFlags.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/api/app/AppBuilder.tsx b/packages/core/src/api/app/AppBuilder.tsx index b29cef79c3..6ea2d0f366 100644 --- a/packages/core/src/api/app/AppBuilder.tsx +++ b/packages/core/src/api/app/AppBuilder.tsx @@ -19,7 +19,7 @@ import { Route, Switch, Redirect } from 'react-router-dom'; import { AppContextProvider } from './AppContext'; import { App } from './types'; import BackstagePlugin from '../plugin/Plugin'; -import { FeatureFlagName } from '../plugin/Plugin/types'; +import { FeatureFlagName } from '../plugin/types'; import { IconComponent, SystemIcons, diff --git a/packages/core/src/api/app/FeatureFlags.tsx b/packages/core/src/api/app/FeatureFlags.tsx index c75e072149..fc0d25dee5 100644 --- a/packages/core/src/api/app/FeatureFlags.tsx +++ b/packages/core/src/api/app/FeatureFlags.tsx @@ -15,7 +15,7 @@ */ // import React, { createContext, useContext, useState, FC } from 'react'; -import { FeatureFlagName } from '../plugin/Plugin/types'; +import { FeatureFlagName } from '../plugin/types'; import { FeatureFlagsApi } from '../apis/definitions/featureFlags'; // TODO: figure out where to put implementations of APIs, both inside apps From c86387ea09c72902380b413a3a5bd3fa80e416f8 Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Thu, 26 Mar 2020 13:36:41 +0100 Subject: [PATCH 076/146] [core] added typescript decorators (experimental) --- packages/core/src/api/app/FeatureFlags.tsx | 20 ++++++---- packages/core/src/testUtils/decorators.ts | 45 ++++++++++++++++++++++ packages/core/src/testUtils/index.js | 1 + packages/core/tsconfig.json | 3 +- 4 files changed, 60 insertions(+), 9 deletions(-) create mode 100644 packages/core/src/testUtils/decorators.ts diff --git a/packages/core/src/api/app/FeatureFlags.tsx b/packages/core/src/api/app/FeatureFlags.tsx index fc0d25dee5..e55660f240 100644 --- a/packages/core/src/api/app/FeatureFlags.tsx +++ b/packages/core/src/api/app/FeatureFlags.tsx @@ -17,13 +17,15 @@ // import React, { createContext, useContext, useState, FC } from 'react'; import { FeatureFlagName } from '../plugin/types'; import { FeatureFlagsApi } from '../apis/definitions/featureFlags'; +import { staticImplements } from '../../testUtils'; // TODO: figure out where to put implementations of APIs, both inside apps // but also in core/separate package. -export class FeatureFlags implements FeatureFlagsApi { +@staticImplements() +export class FeatureFlags { private static readonly localStorageKey = 'featureFlags'; - private static getEnabledFeatureFlags(): Set { + private static getUserEnabledFeatureFlags(): Set { if (!('localStorage' in window)) { throw new Error( 'Feature Flags are not supported on browsers without the Local Storage API', @@ -40,7 +42,9 @@ export class FeatureFlags implements FeatureFlagsApi { } } - private static saveFeatureFlags(flags: Set): void { + private static saveUserEnabledFeatureFlags( + flags: Set, + ): void { if (!('localStorage' in window)) { throw new Error( 'Feature Flags are not supported on browsers without the Local Storage API', @@ -56,18 +60,18 @@ export class FeatureFlags implements FeatureFlagsApi { } static getItem(name: FeatureFlagName): boolean { - return this.getFeatureFlags().has(name); + return this.getUserEnabledFeatureFlags().has(name); } static enable(name: FeatureFlagName): void { - const flags = this.getFeatureFlags(); + const flags = this.getUserEnabledFeatureFlags(); flags.add(name); - this.saveFeatureFlags(flags); + this.saveUserEnabledFeatureFlags(flags); } static disable(name: FeatureFlagName): void { - const flags = this.getFeatureFlags(); + const flags = this.getUserEnabledFeatureFlags(); flags.delete(name); - this.saveFeatureFlags(flags); + this.saveUserEnabledFeatureFlags(flags); } } diff --git a/packages/core/src/testUtils/decorators.ts b/packages/core/src/testUtils/decorators.ts new file mode 100644 index 0000000000..2cea8901b4 --- /dev/null +++ b/packages/core/src/testUtils/decorators.ts @@ -0,0 +1,45 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This utilizes an experimental TypeScript feature called decorators. + * This was originally added to allow us to expose a FeatureFlags API + * with static methods (for backwards compatibility primarily). It takes + * an existing interface and applies the types to static methods in classes. + * + * @see https://www.typescriptlang.org/docs/handbook/decorators.html + * @example + * interface StaticProps { + * append(name: string, extra: string): string; + * reverse(name: string): string; + * } + * + * @staticImplements() + * class StaticPropsClass { + * static append(name, extra) { + * return `${name}${extra}`; + * } + * + * static reverse(name) { + * return name.reverse(); + * } + * } + */ +export function staticImplements() { + return (constructor: U) => { + constructor; + }; +} diff --git a/packages/core/src/testUtils/index.js b/packages/core/src/testUtils/index.js index a0eab3e72b..902ba5d92d 100644 --- a/packages/core/src/testUtils/index.js +++ b/packages/core/src/testUtils/index.js @@ -28,6 +28,7 @@ import { render } from '@testing-library/react'; export { default as Keyboard } from './Keyboard'; export { default as mockBreakpoint } from './mockBreakpoint'; export * from './logCollector'; +export * from './decorators'; export function wrapInTestApp(Component, initialRouterEntries) { const Wrapper = Component instanceof Function ? Component : () => Component; diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json index ad43a0b986..4e024e548f 100644 --- a/packages/core/tsconfig.json +++ b/packages/core/tsconfig.json @@ -2,6 +2,7 @@ "extends": "../../tsconfig.json", "include": ["src"], "compilerOptions": { - "noImplicitAny": false + "noImplicitAny": false, + "experimentalDecorators": true } } From 7ba4f4ca2db2038e20e1bd1b094552cf78a88d2e Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Thu, 26 Mar 2020 15:39:19 +0100 Subject: [PATCH 077/146] [plugins/welcome/WelcomePage] add featureFlags via useApi --- packages/app/src/apis.ts | 9 ++++++++- .../welcome/src/components/WelcomePage/WelcomePage.tsx | 3 +++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index c3315257d8..4d339c3734 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -14,7 +14,13 @@ * limitations under the License. */ -import { ApiHolder, ApiRegistry, errorApiRef } from '@backstage/core'; +import { + ApiHolder, + ApiRegistry, + errorApiRef, + featureFlagsApiRef, + FeatureFlags, +} from '@backstage/core'; import { ErrorDisplayForwarder } from './components/ErrorDisplay/ErrorDisplay'; const builder = ApiRegistry.builder(); @@ -22,5 +28,6 @@ const builder = ApiRegistry.builder(); export const errorDialogForwarder = new ErrorDisplayForwarder(); builder.add(errorApiRef, errorDialogForwarder); +builder.add(featureFlagsApiRef, FeatureFlags); export default builder.build() as ApiHolder; diff --git a/plugins/welcome/src/components/WelcomePage/WelcomePage.tsx b/plugins/welcome/src/components/WelcomePage/WelcomePage.tsx index 69a892a95e..0b6eb71982 100644 --- a/plugins/welcome/src/components/WelcomePage/WelcomePage.tsx +++ b/plugins/welcome/src/components/WelcomePage/WelcomePage.tsx @@ -33,10 +33,13 @@ import { pageTheme, ContentHeader, SupportButton, + featureFlagsApiRef, + useApi, } from '@backstage/core'; import ErrorButton from './ErrorButton'; const WelcomePage: FC<{}> = () => { + const featureFlagsApi = useApi(featureFlagsApiRef); const profile = { givenName: '' }; return ( From 85c3ce7d9a9344c62b8c6370a74850152757cab8 Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Thu, 26 Mar 2020 15:50:45 +0100 Subject: [PATCH 078/146] [core/api/app] added context for registered flags --- packages/core/src/api/app/AppBuilder.tsx | 11 ++++++-- packages/core/src/api/app/FeatureFlags.tsx | 33 +++++++++++++++++++++- packages/core/src/api/index.ts | 2 +- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/packages/core/src/api/app/AppBuilder.tsx b/packages/core/src/api/app/AppBuilder.tsx index 6ea2d0f366..95537d5101 100644 --- a/packages/core/src/api/app/AppBuilder.tsx +++ b/packages/core/src/api/app/AppBuilder.tsx @@ -19,7 +19,7 @@ import { Route, Switch, Redirect } from 'react-router-dom'; import { AppContextProvider } from './AppContext'; import { App } from './types'; import BackstagePlugin from '../plugin/Plugin'; -import { FeatureFlagName } from '../plugin/types'; +import { FeatureFlagsEntry, FeatureFlagsContextProvider } from './FeatureFlags'; import { IconComponent, SystemIcons, @@ -63,7 +63,7 @@ export default class AppBuilder { const app = new AppImpl(this.systemIcons); const routes = new Array(); - const registeredFeatureFlags = new Array<{ name: FeatureFlagName }>(); + const registeredFeatureFlags = new Array(); for (const plugin of this.plugins.values()) { for (const output of plugin.output()) { @@ -114,6 +114,13 @@ export default class AppBuilder { rendered = ; } + rendered = ( + + ); + return () => ; } } diff --git a/packages/core/src/api/app/FeatureFlags.tsx b/packages/core/src/api/app/FeatureFlags.tsx index e55660f240..a5b0c7eaed 100644 --- a/packages/core/src/api/app/FeatureFlags.tsx +++ b/packages/core/src/api/app/FeatureFlags.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -// import React, { createContext, useContext, useState, FC } from 'react'; +import React, { ComponentType, createContext, FC } from 'react'; import { FeatureFlagName } from '../plugin/types'; import { FeatureFlagsApi } from '../apis/definitions/featureFlags'; import { staticImplements } from '../../testUtils'; @@ -75,3 +75,34 @@ export class FeatureFlags { this.saveUserEnabledFeatureFlags(flags); } } + +/** + * Create a shared React context for Feature Flags. + * + * This will be used to propagate all available feature flags to + * Backstage components. This enables viewing all of the components. + */ +export interface FeatureFlagsEntry { + name: FeatureFlagName; +} + +export const FeatureFlagsContext = createContext<{ + registeredFeatureFlags: FeatureFlagsEntry[]; +}>({ + registeredFeatureFlags: [], +}); + +interface Props { + registeredFeatureFlags: FeatureFlagsEntry[]; + children: ComponentType; +} + +export const FeatureFlagsContextProvider: FC = ({ + registeredFeatureFlags, + children, +}) => ( + +); diff --git a/packages/core/src/api/index.ts b/packages/core/src/api/index.ts index e60a7efa18..e1bca9814e 100644 --- a/packages/core/src/api/index.ts +++ b/packages/core/src/api/index.ts @@ -16,5 +16,5 @@ export * from './api'; export * from './apis'; -export { FeatureFlags } from './app/FeatureFlags'; +export { FeatureFlags, FeatureFlagsContext } from './app/FeatureFlags'; export { useApp } from './app/AppContext'; From 758c12101eaebe4dd71c3e600ccd298154954156 Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Thu, 26 Mar 2020 16:00:32 +0100 Subject: [PATCH 079/146] [core/api] added toggle method to FeatureFlags --- packages/core/src/api/apis/definitions/featureFlags.ts | 5 +++++ packages/core/src/api/app/FeatureFlags.tsx | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/packages/core/src/api/apis/definitions/featureFlags.ts b/packages/core/src/api/apis/definitions/featureFlags.ts index c3656ac4cd..dbebec643b 100644 --- a/packages/core/src/api/apis/definitions/featureFlags.ts +++ b/packages/core/src/api/apis/definitions/featureFlags.ts @@ -45,6 +45,11 @@ export type FeatureFlagsApi = { * Disable an registered feature flag. */ disable(name: FeatureFlagName): void; + + /** + * Toggle an registered feature flag on or off. + */ + toggle(name: FeatureFlagName): void; }; export const featureFlagsApiRef = new ApiRef({ diff --git a/packages/core/src/api/app/FeatureFlags.tsx b/packages/core/src/api/app/FeatureFlags.tsx index a5b0c7eaed..55558bfc1a 100644 --- a/packages/core/src/api/app/FeatureFlags.tsx +++ b/packages/core/src/api/app/FeatureFlags.tsx @@ -74,6 +74,10 @@ export class FeatureFlags { flags.delete(name); this.saveUserEnabledFeatureFlags(flags); } + + static toggle(name: FeatureFlagName): void { + (this.getItem(name) ? this.disable : this.enable).bind(this)(name); + } } /** From 71072da838e4c47c32086ebfbc990f786c953c7a Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Thu, 26 Mar 2020 16:07:48 +0100 Subject: [PATCH 080/146] [docs] updated to reflect both useApi and FeatureFlags object --- docs/reference/createPlugin-feature-flags.md | 43 ++++++++++++++++++-- docs/reference/createPlugin.md | 4 +- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/docs/reference/createPlugin-feature-flags.md b/docs/reference/createPlugin-feature-flags.md index 6c3a5f0bc5..6da8d6b084 100644 --- a/docs/reference/createPlugin-feature-flags.md +++ b/docs/reference/createPlugin-feature-flags.md @@ -8,7 +8,25 @@ export type FeatureFlagsHooks = { }; ``` -Then, later, if you want to check if the user has enabled them: +## Using with useApi + +This is the **recommended** way of using the API. It's officially supported by Backstage. To use it, you'll first need to register the `FeatureFlags` API via `ApiRegistry` in your `apis.ts` in your App: + +```tsx +import { + ApiHolder, + ApiRegistry, + featureFlagsApiRef, + FeatureFlags, +} from '@backstage/core'; + +const builder = ApiRegistry.builder(); +builder.add(featureFlagsApiRef, FeatureFlags); + +export default builder.build() as ApiHolder; +``` + +Then, later, you can directly use it via `useApi`: ```tsx import React, { FC } from 'react'; @@ -17,10 +35,27 @@ import { featureFlagsApiRef, useApi } from '@backstage/core'; const ExampleButton: FC<{}> = () => { const featureFlagsApi = useApi(featureFlagsApiRef); + const handleClick = () => featureFlagsApi.enable('enable-example-feature'); - const handleClick = () => { - featureFlagsApi.enable('enable-example-feature'); - }; + return ( + + ); +}; +``` + +## Using directly with the `FeatureFlags` object + +This is **only** for backwards-compatibility support for Spotify's internal Backstage plugins. This may be deprecated in the distant future so generally we advise against using it. + +```tsx +import React, { FC } from 'react'; +import { Button } from '@material-ui/core'; +import { FeatureFlags } from '@backstage/core'; + +const ExampleButton: FC<{}> = () => { + const handleClick = () => FeatureFlags.enable('enable-example-feature'); return ( diff --git a/plugins/welcome/src/plugin.ts b/plugins/welcome/src/plugin.ts index 20ec41d4c2..a54a19d4af 100644 --- a/plugins/welcome/src/plugin.ts +++ b/plugins/welcome/src/plugin.ts @@ -19,7 +19,9 @@ import WelcomePage from './components/WelcomePage'; export default createPlugin({ id: 'welcome', - register({ router }) { + register({ router, featureFlags }) { router.registerRoute('/', WelcomePage); + + featureFlags.registerFeatureFlag('enable-welcome-box'); }, }); From 4969c6046b21682d7e52184c60f65e1c6faf5804 Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Thu, 26 Mar 2020 16:34:12 +0100 Subject: [PATCH 082/146] [core/apis] moved away from ts decorators --- packages/core/src/api/app/FeatureFlags.tsx | 24 ++++++------ packages/core/src/testUtils/decorators.ts | 45 ---------------------- packages/core/tsconfig.json | 3 +- 3 files changed, 12 insertions(+), 60 deletions(-) delete mode 100644 packages/core/src/testUtils/decorators.ts diff --git a/packages/core/src/api/app/FeatureFlags.tsx b/packages/core/src/api/app/FeatureFlags.tsx index 55558bfc1a..6459b38f0d 100644 --- a/packages/core/src/api/app/FeatureFlags.tsx +++ b/packages/core/src/api/app/FeatureFlags.tsx @@ -17,15 +17,13 @@ import React, { ComponentType, createContext, FC } from 'react'; import { FeatureFlagName } from '../plugin/types'; import { FeatureFlagsApi } from '../apis/definitions/featureFlags'; -import { staticImplements } from '../../testUtils'; // TODO: figure out where to put implementations of APIs, both inside apps // but also in core/separate package. -@staticImplements() -export class FeatureFlags { - private static readonly localStorageKey = 'featureFlags'; +class FeatureFlagsImpl implements FeatureFlagsApi { + private readonly localStorageKey = 'featureFlags'; - private static getUserEnabledFeatureFlags(): Set { + private getUserEnabledFeatureFlags(): Set { if (!('localStorage' in window)) { throw new Error( 'Feature Flags are not supported on browsers without the Local Storage API', @@ -42,9 +40,7 @@ export class FeatureFlags { } } - private static saveUserEnabledFeatureFlags( - flags: Set, - ): void { + private saveUserEnabledFeatureFlags(flags: Set): void { if (!('localStorage' in window)) { throw new Error( 'Feature Flags are not supported on browsers without the Local Storage API', @@ -59,32 +55,34 @@ export class FeatureFlags { ); } - static getItem(name: FeatureFlagName): boolean { + getItem(name: FeatureFlagName): boolean { return this.getUserEnabledFeatureFlags().has(name); } - static enable(name: FeatureFlagName): void { + enable(name: FeatureFlagName): void { const flags = this.getUserEnabledFeatureFlags(); flags.add(name); this.saveUserEnabledFeatureFlags(flags); } - static disable(name: FeatureFlagName): void { + disable(name: FeatureFlagName): void { const flags = this.getUserEnabledFeatureFlags(); flags.delete(name); this.saveUserEnabledFeatureFlags(flags); } - static toggle(name: FeatureFlagName): void { + toggle(name: FeatureFlagName): void { (this.getItem(name) ? this.disable : this.enable).bind(this)(name); } } +export const FeatureFlags = new FeatureFlagsImpl(); + /** * Create a shared React context for Feature Flags. * * This will be used to propagate all available feature flags to - * Backstage components. This enables viewing all of the components. + * Backstage components. This enables viewing all of the available flags. */ export interface FeatureFlagsEntry { name: FeatureFlagName; diff --git a/packages/core/src/testUtils/decorators.ts b/packages/core/src/testUtils/decorators.ts deleted file mode 100644 index 2cea8901b4..0000000000 --- a/packages/core/src/testUtils/decorators.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * This utilizes an experimental TypeScript feature called decorators. - * This was originally added to allow us to expose a FeatureFlags API - * with static methods (for backwards compatibility primarily). It takes - * an existing interface and applies the types to static methods in classes. - * - * @see https://www.typescriptlang.org/docs/handbook/decorators.html - * @example - * interface StaticProps { - * append(name: string, extra: string): string; - * reverse(name: string): string; - * } - * - * @staticImplements() - * class StaticPropsClass { - * static append(name, extra) { - * return `${name}${extra}`; - * } - * - * static reverse(name) { - * return name.reverse(); - * } - * } - */ -export function staticImplements() { - return (constructor: U) => { - constructor; - }; -} diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json index 4e024e548f..ad43a0b986 100644 --- a/packages/core/tsconfig.json +++ b/packages/core/tsconfig.json @@ -2,7 +2,6 @@ "extends": "../../tsconfig.json", "include": ["src"], "compilerOptions": { - "noImplicitAny": false, - "experimentalDecorators": true + "noImplicitAny": false } } From 0b49bad43d70a707bbf21731530fe9403f70e4df Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Thu, 26 Mar 2020 16:58:00 +0100 Subject: [PATCH 083/146] [core/api] switch getItem from bool to enum --- packages/core/src/api/apis/definitions/featureFlags.ts | 8 +++++++- packages/core/src/api/app/FeatureFlags.tsx | 9 ++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/core/src/api/apis/definitions/featureFlags.ts b/packages/core/src/api/apis/definitions/featureFlags.ts index dbebec643b..5874bfb052 100644 --- a/packages/core/src/api/apis/definitions/featureFlags.ts +++ b/packages/core/src/api/apis/definitions/featureFlags.ts @@ -28,13 +28,19 @@ import { FeatureFlagName } from '../../plugin/types'; * or unstable upcoming features. Although there will be a common interface for users * to enable and disable feature flags, this API acts as another way to enable/disable. */ + +export enum FeatureFlagState { + NotEnabled = false, + Enabled = true, +} + export type FeatureFlagsApi = { /** * Get the current user's status of a Feature Flag * * @returns bool True if the current user has enabled the feature flag */ - getItem(name: FeatureFlagName): boolean; + getItem(name: FeatureFlagName): FeatureFlagState; /** * Enable an registered feature flag. diff --git a/packages/core/src/api/app/FeatureFlags.tsx b/packages/core/src/api/app/FeatureFlags.tsx index 6459b38f0d..8373e538c2 100644 --- a/packages/core/src/api/app/FeatureFlags.tsx +++ b/packages/core/src/api/app/FeatureFlags.tsx @@ -16,7 +16,10 @@ import React, { ComponentType, createContext, FC } from 'react'; import { FeatureFlagName } from '../plugin/types'; -import { FeatureFlagsApi } from '../apis/definitions/featureFlags'; +import { + FeatureFlagState, + FeatureFlagsApi, +} from '../apis/definitions/featureFlags'; // TODO: figure out where to put implementations of APIs, both inside apps // but also in core/separate package. @@ -55,8 +58,8 @@ class FeatureFlagsImpl implements FeatureFlagsApi { ); } - getItem(name: FeatureFlagName): boolean { - return this.getUserEnabledFeatureFlags().has(name); + getItem(name: FeatureFlagName): FeatureFlagState { + return this.getUserEnabledFeatureFlags().has(name) as FeatureFlagState; } enable(name: FeatureFlagName): void { From 92d091dec86d99c136a169ad40c2d1438f97a8a6 Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Thu, 26 Mar 2020 17:05:53 +0100 Subject: [PATCH 084/146] [core/apis] consolidated methods to get/set --- docs/reference/createPlugin-feature-flags.md | 4 +-- .../src/api/apis/definitions/featureFlags.ts | 16 ++------- packages/core/src/api/app/FeatureFlags.tsx | 35 +++++-------------- .../components/WelcomePage/WelcomePage.tsx | 8 +++-- 4 files changed, 19 insertions(+), 44 deletions(-) diff --git a/docs/reference/createPlugin-feature-flags.md b/docs/reference/createPlugin-feature-flags.md index 6da8d6b084..8226efc129 100644 --- a/docs/reference/createPlugin-feature-flags.md +++ b/docs/reference/createPlugin-feature-flags.md @@ -35,7 +35,7 @@ import { featureFlagsApiRef, useApi } from '@backstage/core'; const ExampleButton: FC<{}> = () => { const featureFlagsApi = useApi(featureFlagsApiRef); - const handleClick = () => featureFlagsApi.enable('enable-example-feature'); + const handleClick = () => featureFlagsApi.set('enable-example-feature', .Enabled); return ( + ); +}; + +export default ToggleFeatureFlagButton; diff --git a/plugins/welcome/src/components/WelcomePage/WelcomePage.tsx b/plugins/welcome/src/components/WelcomePage/WelcomePage.tsx index 7d64b1470a..597ef5044b 100644 --- a/plugins/welcome/src/components/WelcomePage/WelcomePage.tsx +++ b/plugins/welcome/src/components/WelcomePage/WelcomePage.tsx @@ -23,7 +23,6 @@ import { ListItem, ListItemText, Link, - Button, } from '@material-ui/core'; import Timer from '../Timer'; import { @@ -34,13 +33,11 @@ import { pageTheme, ContentHeader, SupportButton, - featureFlagsApiRef, - useApi, } from '@backstage/core'; import ErrorButton from './ErrorButton'; +import ToggleFeatureFlagButton from './ToggleFeatureFlagButton'; const WelcomePage: FC<{}> = () => { - const featureFlagsApi = useApi(featureFlagsApiRef); const profile = { givenName: '' }; return ( @@ -127,22 +124,7 @@ const WelcomePage: FC<{}> = () => {

- + From abe68bc55b9561f32f0812fad92de2dfc33fc077 Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Thu, 26 Mar 2020 21:21:41 +0100 Subject: [PATCH 087/146] [core] fixed typescript issues --- .../src/api/apis/definitions/featureFlags.ts | 4 ++-- packages/core/src/api/app/AppBuilder.tsx | 2 +- packages/core/src/api/app/FeatureFlags.tsx | 16 +++++++++++----- packages/core/src/api/plugin/Plugin.tsx | 4 ++++ packages/core/src/testUtils/index.js | 1 - 5 files changed, 18 insertions(+), 9 deletions(-) diff --git a/packages/core/src/api/apis/definitions/featureFlags.ts b/packages/core/src/api/apis/definitions/featureFlags.ts index 750798d4b1..952e2b0121 100644 --- a/packages/core/src/api/apis/definitions/featureFlags.ts +++ b/packages/core/src/api/apis/definitions/featureFlags.ts @@ -30,8 +30,8 @@ import { FeatureFlagName } from '../../plugin/types'; */ export enum FeatureFlagState { - NotEnabled = false, - Enabled = true, + NotEnabled = 0, + Enabled = 1, } export type FeatureFlagsApi = { diff --git a/packages/core/src/api/app/AppBuilder.tsx b/packages/core/src/api/app/AppBuilder.tsx index c8a56876c9..ccd9982249 100644 --- a/packages/core/src/api/app/AppBuilder.tsx +++ b/packages/core/src/api/app/AppBuilder.tsx @@ -91,7 +91,7 @@ export default class AppBuilder { } case 'feature-flag': { registeredFeatureFlags.push({ - pluginId: plugin.config.id, + pluginId: plugin.getId(), name: output.name, }); break; diff --git a/packages/core/src/api/app/FeatureFlags.tsx b/packages/core/src/api/app/FeatureFlags.tsx index 708f4f50fc..74060beb6d 100644 --- a/packages/core/src/api/app/FeatureFlags.tsx +++ b/packages/core/src/api/app/FeatureFlags.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React, { ComponentType, createContext, FC } from 'react'; +import React, { ReactNode, createContext, FC } from 'react'; import { FeatureFlagName } from '../plugin/types'; import { FeatureFlagState, @@ -37,14 +37,20 @@ class FeatureFlagsImpl implements FeatureFlagsApi { const featureFlagsJson = window.localStorage.getItem( this.localStorageKey, ); - return new Set<>(Object.keys(JSON.parse(featureFlagsJson!))); + return new Set( + Object.keys(JSON.parse(featureFlagsJson!)), + ); } catch (err) { - return new Set<>(); + return new Set(); } } get(name: FeatureFlagName): FeatureFlagState { - return this.getUserEnabledFeatureFlags().has(name) as FeatureFlagState; + if (this.getUserEnabledFeatureFlags().has(name)) { + return FeatureFlagState.Enabled; + } + + return FeatureFlagState.NotEnabled; } set(name: FeatureFlagName, state: FeatureFlagState): void { @@ -83,7 +89,7 @@ export const FeatureFlagsContext = createContext<{ interface Props { registeredFeatureFlags: FeatureFlagsEntry[]; - children: ComponentType; + children: ReactNode; } export const FeatureFlagsContextProvider: FC = ({ diff --git a/packages/core/src/api/plugin/Plugin.tsx b/packages/core/src/api/plugin/Plugin.tsx index aa1ff417b7..ddb95bfff8 100644 --- a/packages/core/src/api/plugin/Plugin.tsx +++ b/packages/core/src/api/plugin/Plugin.tsx @@ -64,6 +64,10 @@ export default class Plugin { constructor(private readonly config: PluginConfig) {} + getId(): string { + return this.config.id; + } + output(): PluginOutput[] { if (this.storedOutput) { return this.storedOutput; diff --git a/packages/core/src/testUtils/index.js b/packages/core/src/testUtils/index.js index 902ba5d92d..a0eab3e72b 100644 --- a/packages/core/src/testUtils/index.js +++ b/packages/core/src/testUtils/index.js @@ -28,7 +28,6 @@ import { render } from '@testing-library/react'; export { default as Keyboard } from './Keyboard'; export { default as mockBreakpoint } from './mockBreakpoint'; export * from './logCollector'; -export * from './decorators'; export function wrapInTestApp(Component, initialRouterEntries) { const Wrapper = Component instanceof Function ? Component : () => Component; From 4e38f03221cb4247d302645e35e9e325484a2ef4 Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Fri, 27 Mar 2020 11:09:07 +0100 Subject: [PATCH 088/146] [core/api] added tests for FeatureFlags.tsx --- .../core/src/api/app/FeatureFlags.test.tsx | 155 ++++++++++++++++++ packages/core/src/api/app/FeatureFlags.tsx | 30 +++- 2 files changed, 183 insertions(+), 2 deletions(-) create mode 100644 packages/core/src/api/app/FeatureFlags.test.tsx diff --git a/packages/core/src/api/app/FeatureFlags.test.tsx b/packages/core/src/api/app/FeatureFlags.test.tsx new file mode 100644 index 0000000000..2159d05e65 --- /dev/null +++ b/packages/core/src/api/app/FeatureFlags.test.tsx @@ -0,0 +1,155 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { useContext } from 'react'; +import { render } from '@testing-library/react'; +import { + FeatureFlags, + FeatureFlagsContext, + FeatureFlagsContextProvider, +} from './FeatureFlags'; +import { FeatureFlagState } from '../apis/definitions/featureFlags'; + +describe('FeatureFlags', () => { + beforeEach(() => { + window.localStorage.clear(); + }); + + describe('#get', () => { + it('defaults to .NotEnabled', () => { + expect(FeatureFlags.get('enable-feature-flag')).toBe( + FeatureFlagState.NotEnabled, + ); + }); + + it('returns a .Enabled state', () => { + FeatureFlags.set('enable-feature-flag', FeatureFlagState.Enabled); + expect(FeatureFlags.get('enable-feature-flag')).toBe( + FeatureFlagState.Enabled, + ); + }); + + it('returns a .NotEnabled state', () => { + FeatureFlags.set('enable-feature-flag', FeatureFlagState.NotEnabled); + expect(FeatureFlags.get('enable-feature-flag')).toBe( + FeatureFlagState.NotEnabled, + ); + }); + }); + + describe('#set', () => { + it('fails if name is less than three characters', () => { + expect(() => { + FeatureFlags.set('ab', FeatureFlagState.NotEnabled); + }).toThrow(/minimum length of three characters/i); + }); + + it('fails if name is greater than 150 characters', () => { + expect(() => { + FeatureFlags.set( + 'loremipsumdolorsitametconsecteturadipiscingelitnuncvitaeportaexaullamcorperturpismaurisutmattisnequemorbisediaculisauguevivamuspulvinarcursuseratblandithendreritquisqueuttinciduntmagnavestibulumblanditaugueat', + FeatureFlagState.NotEnabled, + ); + }).toThrow(/not exceed 150 characters/i); + }); + + it('fails if name does not start with a lowercase letter', () => { + expect(() => { + FeatureFlags.set('123456789', FeatureFlagState.NotEnabled); + }).toThrow(/start with a lowercase letter/i); + }); + + it('fails if name contains characters other than lowercase letters, numbers and hyphens', () => { + expect(() => { + FeatureFlags.set('Invalid_Feature_Flag', FeatureFlagState.NotEnabled); + }).toThrow(/only contain lowercase letters, numbers and hyphens/i); + }); + + it('fails if state is not recognized from FeatureFlagState', () => { + expect(() => { + // @ts-ignore + FeatureFlags.set('valid-feature-flag', 'invalid state'); + }).toThrow(/requires a recognized value from the FeatureFlagState/i); + }); + + it('sets a feature flag to enabled', () => { + expect(window.localStorage.featureFlags).toBeUndefined(); + FeatureFlags.set('valid-feature-flag', FeatureFlagState.Enabled); + expect(window.localStorage.featureFlags).toBe( + '{"valid-feature-flag":true}', + ); + }); + + it('sets a feature flag to disabled', () => { + expect(window.localStorage.featureFlags).toBeUndefined(); + FeatureFlags.set('valid-feature-flag', FeatureFlagState.NotEnabled); + expect(window.localStorage.featureFlags).toBe('{}'); + }); + + it('sets a feature flag to disabled then enabled', () => { + expect(window.localStorage.featureFlags).toBeUndefined(); + FeatureFlags.set('valid-feature-flag', FeatureFlagState.NotEnabled); + FeatureFlags.set('valid-feature-flag', FeatureFlagState.Enabled); + expect(window.localStorage.featureFlags).toBe( + '{"valid-feature-flag":true}', + ); + }); + + it('sets multiple feature flags', () => { + expect(window.localStorage.featureFlags).toBeUndefined(); + FeatureFlags.set('valid-feature-flag', FeatureFlagState.Enabled); + FeatureFlags.set('another-valid-feature-flag', FeatureFlagState.Enabled); + expect(window.localStorage.featureFlags).toBe( + '{"valid-feature-flag":true,"another-valid-feature-flag":true}', + ); + }); + }); +}); + +describe('FeatureFlagsContext', () => { + it('returns an empty list without the context', () => { + expect.assertions(1); + + const Component = () => { + const { registeredFeatureFlags } = useContext(FeatureFlagsContext); + expect(registeredFeatureFlags).toEqual([]); + return null; + }; + + render(); + }); + + it('returns a list of registered feature flags', () => { + expect.assertions(1); + + const mockFeatureFlags = [ + { name: 'feature-flag-one', pluginId: 'plugin-one' }, + { name: 'feature-flag-two', pluginId: 'plugin-two' }, + ]; + + const Component = () => { + const { registeredFeatureFlags } = useContext(FeatureFlagsContext); + expect(registeredFeatureFlags).toBe(mockFeatureFlags); + return null; + }; + + render( + + + , + ); + }); +}); diff --git a/packages/core/src/api/app/FeatureFlags.tsx b/packages/core/src/api/app/FeatureFlags.tsx index 74060beb6d..2b543db216 100644 --- a/packages/core/src/api/app/FeatureFlags.tsx +++ b/packages/core/src/api/app/FeatureFlags.tsx @@ -56,8 +56,34 @@ class FeatureFlagsImpl implements FeatureFlagsApi { set(name: FeatureFlagName, state: FeatureFlagState): void { const flags = this.getUserEnabledFeatureFlags(); - if (state === FeatureFlagState.NotEnabled) flags.delete(name); - if (state === FeatureFlagState.Enabled) flags.add(name); + if (name.length < 3) { + throw new Error( + 'The `name` argument must have a minimum length of three characters.', + ); + } + + if (name.length > 150) { + throw new Error('The `name` argument must not exceed 150 characters.'); + } + + if (!name.match(/^[a-z]+[a-z0-9-]+$/)) { + throw new Error( + 'The `name` argument must start with a lowercase letter and only contain lowercase letters, numbers and hyphens.' + + 'Examples: feature-flag-one, alpha, release-2020', + ); + } + + if (state === FeatureFlagState.Enabled) { + flags.add(name); + } else if (state === FeatureFlagState.NotEnabled) { + flags.delete(name); + } else { + throw new Error( + 'The `state` argument requires a recognized value from the FeatureFlagState enum. ' + + 'Please check the Backstage documentation to see all the available options.' + + 'Example values: FeatureFlagState.NotEnabled, FeatureFlagState.Enabled', + ); + } window.localStorage.setItem( this.localStorageKey, From 24f987b29b1340389bc322e50c72e9e95f4203c6 Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Fri, 27 Mar 2020 11:35:38 +0100 Subject: [PATCH 089/146] [core/api/app] split name validation into function --- .../src/api/apis/definitions/featureFlags.ts | 8 ++++ packages/core/src/api/app/FeatureFlags.tsx | 43 ++++++++++++------- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/packages/core/src/api/apis/definitions/featureFlags.ts b/packages/core/src/api/apis/definitions/featureFlags.ts index 952e2b0121..15c9ecb084 100644 --- a/packages/core/src/api/apis/definitions/featureFlags.ts +++ b/packages/core/src/api/apis/definitions/featureFlags.ts @@ -35,6 +35,14 @@ export enum FeatureFlagState { } export type FeatureFlagsApi = { + /** + * Check the feature flag name convention. Used in the + * `registerFeatureFlag` method as well as in the `set` method. + * + * @returns string[] errors List of errors as string. Empty array if no errors. + */ + checkFeatureFlagNameErrors(name: FeatureFlagName): string[]; + /** * Get the current user's status of a Feature Flag * diff --git a/packages/core/src/api/app/FeatureFlags.tsx b/packages/core/src/api/app/FeatureFlags.tsx index 2b543db216..8d3d366906 100644 --- a/packages/core/src/api/app/FeatureFlags.tsx +++ b/packages/core/src/api/app/FeatureFlags.tsx @@ -45,6 +45,31 @@ class FeatureFlagsImpl implements FeatureFlagsApi { } } + // We don't make this private as we need this to validate + // in the `registerFeatureFlag` method in the Plugin API. + checkFeatureFlagNameErrors(name: FeatureFlagName): string[] { + const errors = []; + + if (name.length < 3) { + errors.push( + 'The `name` argument must have a minimum length of three characters.', + ); + } + + if (name.length > 150) { + errors.push('The `name` argument must not exceed 150 characters.'); + } + + if (!name.match(/^[a-z]+[a-z0-9-]+$/)) { + errors.push( + 'The `name` argument must start with a lowercase letter and only contain lowercase letters, numbers and hyphens.' + + 'Examples: feature-flag-one, alpha, release-2020', + ); + } + + return errors; + } + get(name: FeatureFlagName): FeatureFlagState { if (this.getUserEnabledFeatureFlags().has(name)) { return FeatureFlagState.Enabled; @@ -54,23 +79,11 @@ class FeatureFlagsImpl implements FeatureFlagsApi { } set(name: FeatureFlagName, state: FeatureFlagState): void { + const errors = this.checkFeatureFlagNameErrors(name); const flags = this.getUserEnabledFeatureFlags(); - if (name.length < 3) { - throw new Error( - 'The `name` argument must have a minimum length of three characters.', - ); - } - - if (name.length > 150) { - throw new Error('The `name` argument must not exceed 150 characters.'); - } - - if (!name.match(/^[a-z]+[a-z0-9-]+$/)) { - throw new Error( - 'The `name` argument must start with a lowercase letter and only contain lowercase letters, numbers and hyphens.' + - 'Examples: feature-flag-one, alpha, release-2020', - ); + if (errors.length > 0) { + throw new Error(errors[0]); } if (state === FeatureFlagState.Enabled) { From 9aaad5d6608c8cb9f588d2ad85f8ae057de7a167 Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Fri, 27 Mar 2020 11:52:14 +0100 Subject: [PATCH 090/146] [core/api] add name validation in registerFeatureFlag --- packages/core/src/api/app/FeatureFlags.tsx | 2 +- packages/core/src/api/plugin/Plugin.tsx | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/core/src/api/app/FeatureFlags.tsx b/packages/core/src/api/app/FeatureFlags.tsx index 8d3d366906..b8799e5a71 100644 --- a/packages/core/src/api/app/FeatureFlags.tsx +++ b/packages/core/src/api/app/FeatureFlags.tsx @@ -62,7 +62,7 @@ class FeatureFlagsImpl implements FeatureFlagsApi { if (!name.match(/^[a-z]+[a-z0-9-]+$/)) { errors.push( - 'The `name` argument must start with a lowercase letter and only contain lowercase letters, numbers and hyphens.' + + 'The `name` argument must start with a lowercase letter and only contain lowercase letters, numbers and hyphens. ' + 'Examples: feature-flag-one, alpha, release-2020', ); } diff --git a/packages/core/src/api/plugin/Plugin.tsx b/packages/core/src/api/plugin/Plugin.tsx index ddb95bfff8..d342cb2429 100644 --- a/packages/core/src/api/plugin/Plugin.tsx +++ b/packages/core/src/api/plugin/Plugin.tsx @@ -21,6 +21,7 @@ import { RouteOptions, FeatureFlagName, } from './types'; +import { FeatureFlags } from '../app/FeatureFlags'; import { Widget } from '../widgetView/types'; export type PluginConfig = { @@ -76,6 +77,7 @@ export default class Plugin { return []; } + const pluginId = this.getId(); const outputs = new Array(); this.config.register({ @@ -94,6 +96,14 @@ export default class Plugin { }, featureFlags: { registerFeatureFlag(name) { + const errors = FeatureFlags.checkFeatureFlagNameErrors(name); + + if (errors.length > 0) { + throw new Error( + `Failed to register '${name}' feature flag in '${pluginId}' plugin: ${errors[0]}`, + ); + } + outputs.push({ type: 'feature-flag', name }); }, }, From 5157904031b5101aa46d87f5615faeae575f595e Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Fri, 27 Mar 2020 12:15:50 +0100 Subject: [PATCH 091/146] [core/api] s/registeredFeatureFlags/featureFlags/g --- packages/core/src/api/app/AppBuilder.tsx | 2 +- .../core/src/api/app/FeatureFlags.test.tsx | 10 +-- packages/core/src/api/app/FeatureFlags.tsx | 78 +++++++++++-------- 3 files changed, 51 insertions(+), 39 deletions(-) diff --git a/packages/core/src/api/app/AppBuilder.tsx b/packages/core/src/api/app/AppBuilder.tsx index ccd9982249..84317a735a 100644 --- a/packages/core/src/api/app/AppBuilder.tsx +++ b/packages/core/src/api/app/AppBuilder.tsx @@ -119,7 +119,7 @@ export default class AppBuilder { rendered = ( ); diff --git a/packages/core/src/api/app/FeatureFlags.test.tsx b/packages/core/src/api/app/FeatureFlags.test.tsx index 2159d05e65..3e3bb3e50d 100644 --- a/packages/core/src/api/app/FeatureFlags.test.tsx +++ b/packages/core/src/api/app/FeatureFlags.test.tsx @@ -124,8 +124,8 @@ describe('FeatureFlagsContext', () => { expect.assertions(1); const Component = () => { - const { registeredFeatureFlags } = useContext(FeatureFlagsContext); - expect(registeredFeatureFlags).toEqual([]); + const { featureFlags } = useContext(FeatureFlagsContext); + expect(featureFlags).toEqual([]); return null; }; @@ -141,13 +141,13 @@ describe('FeatureFlagsContext', () => { ]; const Component = () => { - const { registeredFeatureFlags } = useContext(FeatureFlagsContext); - expect(registeredFeatureFlags).toBe(mockFeatureFlags); + const { featureFlags } = useContext(FeatureFlagsContext); + expect(featureFlags).toBe(mockFeatureFlags); return null; }; render( - + , ); diff --git a/packages/core/src/api/app/FeatureFlags.tsx b/packages/core/src/api/app/FeatureFlags.tsx index b8799e5a71..9921486999 100644 --- a/packages/core/src/api/app/FeatureFlags.tsx +++ b/packages/core/src/api/app/FeatureFlags.tsx @@ -14,13 +14,57 @@ * limitations under the License. */ -import React, { ReactNode, createContext, FC } from 'react'; +import React, { + ReactNode, + createContext, + useContext, + useState, + FC, +} from 'react'; import { FeatureFlagName } from '../plugin/types'; import { FeatureFlagState, FeatureFlagsApi, } from '../apis/definitions/featureFlags'; +/** + * Create a shared React context for Feature Flags. + * + * This will be used to propagate all available feature flags to + * Backstage components. This enables viewing all of the available flags. + */ +export interface FeatureFlagsEntry { + pluginId: string; + name: FeatureFlagName; + userEnabled: boolean; +} + +export interface IFeatureFlagsContext { + featureFlags: FeatureFlagsEntry[]; +} + +export const FeatureFlagsContext = createContext({ + featureFlags: [], +}); + +export const FeatureFlagsContextProvider: FC<{ + featureFlags: FeatureFlagsEntry[]; + children: ReactNode; +}> = ({ featureFlags, children }) => { + const [userEnabledFlags, setUserEnabledFlags] = useState([]); + + return ( + + ); +}; + +/** + * Create the FeatureFlags implementation based on the API. + */ + // TODO: figure out where to put implementations of APIs, both inside apps // but also in core/separate package. class FeatureFlagsImpl implements FeatureFlagsApi { @@ -108,35 +152,3 @@ class FeatureFlagsImpl implements FeatureFlagsApi { } export const FeatureFlags = new FeatureFlagsImpl(); - -/** - * Create a shared React context for Feature Flags. - * - * This will be used to propagate all available feature flags to - * Backstage components. This enables viewing all of the available flags. - */ -export interface FeatureFlagsEntry { - pluginId: string; - name: FeatureFlagName; -} - -export const FeatureFlagsContext = createContext<{ - registeredFeatureFlags: FeatureFlagsEntry[]; -}>({ - registeredFeatureFlags: [], -}); - -interface Props { - registeredFeatureFlags: FeatureFlagsEntry[]; - children: ReactNode; -} - -export const FeatureFlagsContextProvider: FC = ({ - registeredFeatureFlags, - children, -}) => ( - -); From c1f5b2abc9e2a2a5284407c87d7f234481b892c3 Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Fri, 27 Mar 2020 12:49:48 +0100 Subject: [PATCH 092/146] [core/apis] switch to useFeatureFlag React Hook --- .../src/api/apis/definitions/featureFlags.ts | 25 ++-- packages/core/src/api/app/AppBuilder.tsx | 4 +- packages/core/src/api/app/FeatureFlags.tsx | 132 ++++++++++++++---- .../WelcomePage/ToggleFeatureFlagButton.tsx | 19 ++- 4 files changed, 126 insertions(+), 54 deletions(-) diff --git a/packages/core/src/api/apis/definitions/featureFlags.ts b/packages/core/src/api/apis/definitions/featureFlags.ts index 15c9ecb084..e3eaeefdfb 100644 --- a/packages/core/src/api/apis/definitions/featureFlags.ts +++ b/packages/core/src/api/apis/definitions/featureFlags.ts @@ -36,24 +36,27 @@ export enum FeatureFlagState { export type FeatureFlagsApi = { /** - * Check the feature flag name convention. Used in the - * `registerFeatureFlag` method as well as in the `set` method. + * Get a list of the user's currently enabled feature flags. + * Reads directly from window.localStorage Browser API. + * + * @returns string[] enabledFeatureFlags List of feature flags enabled by the current user + */ + getEnabledFeatureFlags(): Set; + + /** + * Check the feature flag name convention. + * Used in the `registerFeatureFlag` method as well as in the `set` method. * * @returns string[] errors List of errors as string. Empty array if no errors. */ checkFeatureFlagNameErrors(name: FeatureFlagName): string[]; /** - * Get the current user's status of a Feature Flag - * - * @returns bool True if the current user has enabled the feature flag + * Use Feature Flags as a React Hook. */ - get(name: FeatureFlagName): FeatureFlagState; - - /** - * Set the state of a Feature Flag - */ - set(name: FeatureFlagName, state: FeatureFlagState): void; + useFeatureFlag( + name: FeatureFlagName, + ): [FeatureFlagState, (state: FeatureFlagState) => void]; }; export const featureFlagsApiRef = new ApiRef({ diff --git a/packages/core/src/api/app/AppBuilder.tsx b/packages/core/src/api/app/AppBuilder.tsx index 84317a735a..bd40565260 100644 --- a/packages/core/src/api/app/AppBuilder.tsx +++ b/packages/core/src/api/app/AppBuilder.tsx @@ -63,7 +63,7 @@ export default class AppBuilder { const app = new AppImpl(this.systemIcons); const routes = new Array(); - const registeredFeatureFlags = new Array(); + const registeredFeatureFlags = new Set(); for (const plugin of this.plugins.values()) { for (const output of plugin.output()) { @@ -90,7 +90,7 @@ export default class AppBuilder { break; } case 'feature-flag': { - registeredFeatureFlags.push({ + registeredFeatureFlags.add({ pluginId: plugin.getId(), name: output.name, }); diff --git a/packages/core/src/api/app/FeatureFlags.tsx b/packages/core/src/api/app/FeatureFlags.tsx index 9921486999..e5a2063621 100644 --- a/packages/core/src/api/app/FeatureFlags.tsx +++ b/packages/core/src/api/app/FeatureFlags.tsx @@ -19,6 +19,7 @@ import React, { createContext, useContext, useState, + useEffect, FC, } from 'react'; import { FeatureFlagName } from '../plugin/types'; @@ -36,26 +37,50 @@ import { export interface FeatureFlagsEntry { pluginId: string; name: FeatureFlagName; - userEnabled: boolean; } export interface IFeatureFlagsContext { - featureFlags: FeatureFlagsEntry[]; + featureFlags: Set; + enabledFeatureFlags: Set; + refreshEnabledFeatureFlags: () => void; } export const FeatureFlagsContext = createContext({ - featureFlags: [], + featureFlags: new Set(), + enabledFeatureFlags: new Set(), + refreshEnabledFeatureFlags: () => { + throw new Error( + 'The refreshEnabledFeatureFlags method is not implemented as it is not called from within the FeatureFlagsContext context within React. ' + + 'See the Backstage documentation for examples on how to use the Feature Flags API.', + ); + }, }); export const FeatureFlagsContextProvider: FC<{ - featureFlags: FeatureFlagsEntry[]; + featureFlags: Set; children: ReactNode; }> = ({ featureFlags, children }) => { - const [userEnabledFlags, setUserEnabledFlags] = useState([]); + const [enabledFeatureFlags, setEnabledFeatureFlags] = useState< + Set + >(new Set()); + + const refreshEnabledFeatureFlags = () => { + // eslint-disable-next-line no-use-before-define + setEnabledFeatureFlags(FeatureFlags.getEnabledFeatureFlags()); + }; + + // Initially populate our setEnabledFeatureFlags + useEffect(() => { + refreshEnabledFeatureFlags(); + }, []); return ( ); @@ -70,7 +95,46 @@ export const FeatureFlagsContextProvider: FC<{ class FeatureFlagsImpl implements FeatureFlagsApi { private readonly localStorageKey = 'featureFlags'; - private getUserEnabledFeatureFlags(): Set { + private get( + enabledFeatureFlags: Set, + name: FeatureFlagName, + ): FeatureFlagState { + if (enabledFeatureFlags.has(name)) { + return FeatureFlagState.Enabled; + } + + return FeatureFlagState.NotEnabled; + } + + private set(name: FeatureFlagName, state: FeatureFlagState): void { + const errors = this.checkFeatureFlagNameErrors(name); + const flags = this.getEnabledFeatureFlags(); + + if (errors.length > 0) { + throw new Error(errors[0]); + } + + if (state === FeatureFlagState.Enabled) { + flags.add(name); + } else if (state === FeatureFlagState.NotEnabled) { + flags.delete(name); + } else { + throw new Error( + 'The `state` argument requires a recognized value from the FeatureFlagState enum. ' + + 'Please check the Backstage documentation to see all the available options.' + + 'Example values: FeatureFlagState.NotEnabled, FeatureFlagState.Enabled', + ); + } + + window.localStorage.setItem( + this.localStorageKey, + JSON.stringify( + [...flags].reduce((list, flag) => ({ ...list, [flag]: true }), {}), + ), + ); + } + + getEnabledFeatureFlags(): Set { if (!('localStorage' in window)) { throw new Error( 'Feature Flags are not supported on browsers without the Local Storage API', @@ -114,40 +178,46 @@ class FeatureFlagsImpl implements FeatureFlagsApi { return errors; } - get(name: FeatureFlagName): FeatureFlagState { - if (this.getUserEnabledFeatureFlags().has(name)) { - return FeatureFlagState.Enabled; + useFeatureFlag( + name: FeatureFlagName, + ): [FeatureFlagState, (state: FeatureFlagState) => void] { + // Check for context + const context = useContext(FeatureFlagsContext); + if (!context) { + throw new Error( + 'No FeatureFlagsContext found. ' + + 'Please use this React Hook in the context of your ', + ); } - return FeatureFlagState.NotEnabled; - } - - set(name: FeatureFlagName, state: FeatureFlagState): void { - const errors = this.checkFeatureFlagNameErrors(name); - const flags = this.getUserEnabledFeatureFlags(); - + // Check for errors + // eslint-disable-next-line no-use-before-define + const errors = FeatureFlags.checkFeatureFlagNameErrors(name); if (errors.length > 0) { throw new Error(errors[0]); } - if (state === FeatureFlagState.Enabled) { - flags.add(name); - } else if (state === FeatureFlagState.NotEnabled) { - flags.delete(name); - } else { + // Check if the feature flag is registered + const allFlagNames = [...context.featureFlags].map(flag => flag.name); + if (!allFlagNames.includes(name)) { throw new Error( - 'The `state` argument requires a recognized value from the FeatureFlagState enum. ' + - 'Please check the Backstage documentation to see all the available options.' + - 'Example values: FeatureFlagState.NotEnabled, FeatureFlagState.Enabled', + `The '${name}' feature flag is not registered by any plugin. ` + + `See the 'registerFeatureFlag' method in the Plugin API (or in your plugin.ts file) on how to register Feature Flags.`, ); } - window.localStorage.setItem( - this.localStorageKey, - JSON.stringify( - [...flags].reduce((list, flag) => ({ ...list, [flag]: true }), {}), - ), - ); + // eslint-disable-next-line no-use-before-define + const currentState = FeatureFlags.get(context.enabledFeatureFlags, name); + const setState = (state: FeatureFlagState): void => { + // Set the value + // eslint-disable-next-line no-use-before-define + FeatureFlags.set(name, state); + + // Now update the global state + context.refreshEnabledFeatureFlags(); + }; + + return [currentState, setState]; } } diff --git a/plugins/welcome/src/components/WelcomePage/ToggleFeatureFlagButton.tsx b/plugins/welcome/src/components/WelcomePage/ToggleFeatureFlagButton.tsx index f9cf68f20c..a940b76263 100644 --- a/plugins/welcome/src/components/WelcomePage/ToggleFeatureFlagButton.tsx +++ b/plugins/welcome/src/components/WelcomePage/ToggleFeatureFlagButton.tsx @@ -19,15 +19,15 @@ import { Button } from '@material-ui/core'; import { FeatureFlagState, featureFlagsApiRef, useApi } from '@backstage/core'; const ToggleFeatureFlagButton: FC<{}> = () => { - const featureFlagsApi = useApi(featureFlagsApiRef); + const { useFeatureFlag } = useApi(featureFlagsApiRef); + const [flagState, setFlagState] = useFeatureFlag('enable-welcome-box'); const handleClick = () => { - const isEnabled = featureFlagsApi.get('enable-welcome-box'); - featureFlagsApi.set( - 'enable-welcome-box', - isEnabled ? FeatureFlagState.NotEnabled : FeatureFlagState.Enabled, - ); - window.location.reload(); + if (flagState === FeatureFlagState.Enabled) { + setFlagState(FeatureFlagState.NotEnabled); + } else { + setFlagState(FeatureFlagState.Enabled); + } }; return ( @@ -37,10 +37,9 @@ const ToggleFeatureFlagButton: FC<{}> = () => { onClick={handleClick} data-testid="button-switch-feature-flag-state" > - {featureFlagsApi.get('enable-welcome-box') === - FeatureFlagState.NotEnabled && + {flagState === FeatureFlagState.NotEnabled && 'Enable "enable-welcome-box" feature flag'} - {featureFlagsApi.get('enable-welcome-box') === FeatureFlagState.Enabled && + {flagState === FeatureFlagState.Enabled && 'Disable "enable-welcome-box" feature flag'} ); From e78680fe0340fdc984df797d290275c98f3d1c1e Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Fri, 27 Mar 2020 13:04:59 +0100 Subject: [PATCH 093/146] [plugins/welcome] fixed ToggleFeatureFlagButton test --- packages/core/src/api/index.ts | 6 +++- .../ToggleFeatureFlagButton.test.tsx | 35 +++++++++++++------ 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/packages/core/src/api/index.ts b/packages/core/src/api/index.ts index e1bca9814e..4c77e94984 100644 --- a/packages/core/src/api/index.ts +++ b/packages/core/src/api/index.ts @@ -16,5 +16,9 @@ export * from './api'; export * from './apis'; -export { FeatureFlags, FeatureFlagsContext } from './app/FeatureFlags'; +export { + FeatureFlags, + FeatureFlagsContext, + FeatureFlagsContextProvider, +} from './app/FeatureFlags'; export { useApp } from './app/AppContext'; diff --git a/plugins/welcome/src/components/WelcomePage/ToggleFeatureFlagButton.test.tsx b/plugins/welcome/src/components/WelcomePage/ToggleFeatureFlagButton.test.tsx index 8efb25f0a6..f9be280f50 100644 --- a/plugins/welcome/src/components/WelcomePage/ToggleFeatureFlagButton.test.tsx +++ b/plugins/welcome/src/components/WelcomePage/ToggleFeatureFlagButton.test.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React from 'react'; +import React, { ReactNode } from 'react'; import { render, fireEvent } from '@testing-library/react'; import ToggleFeatureFlagButton from './ToggleFeatureFlagButton'; import { @@ -22,8 +22,29 @@ import { featureFlagsApiRef, ApiProvider, FeatureFlags, + FeatureFlagsContextProvider, } from '@backstage/core'; +function withFeatureFlags(children: ReactNode) { + const featureFlags = new Set([ + { pluginId: 'welcome', name: 'enable-welcome-box' }, + ]); + + return ( + + {children} + + ); +} + +function withApiRegistry(children: ReactNode) { + return ( + + {children} + + ); +} + describe('ToggleFeatureFlagButton', () => { beforeEach(() => { window.localStorage.clear(); @@ -31,11 +52,7 @@ describe('ToggleFeatureFlagButton', () => { it('should enable the feature flag', () => { const rendered = render( - - - , + withFeatureFlags(withApiRegistry()), ); const button = rendered.getByTestId('button-switch-feature-flag-state'); @@ -50,11 +67,7 @@ describe('ToggleFeatureFlagButton', () => { it('should disable the feature flag', () => { const rendered = render( - - - , + withFeatureFlags(withApiRegistry()), ); const button = rendered.getByTestId('button-switch-feature-flag-state'); From a8f304dbfbb7ad96da67a8c490e64e80136b920d Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Fri, 27 Mar 2020 13:13:46 +0100 Subject: [PATCH 094/146] [docs/reference] updated to reflect React Hooks changes --- docs/reference/createPlugin-feature-flags.md | 44 +- .../core/src/api/app/FeatureFlags.test.tsx | 378 ++++++++++++++---- packages/core/src/api/app/FeatureFlags.tsx | 10 +- 3 files changed, 325 insertions(+), 107 deletions(-) diff --git a/docs/reference/createPlugin-feature-flags.md b/docs/reference/createPlugin-feature-flags.md index 8226efc129..bbf7f17c32 100644 --- a/docs/reference/createPlugin-feature-flags.md +++ b/docs/reference/createPlugin-feature-flags.md @@ -8,9 +8,23 @@ export type FeatureFlagsHooks = { }; ``` +Here's a code sample: + +```typescript +import { createPlugin } from '@backstage/core'; + +export default createPlugin({ + id: 'welcome', + register({ router, featureFlags }) { + // router.registerRoute('/', Component); + featureFlags.registerFeatureFlag('enable-example-feature'); + }, +}); +``` + ## Using with useApi -This is the **recommended** way of using the API. It's officially supported by Backstage. To use it, you'll first need to register the `FeatureFlags` API via `ApiRegistry` in your `apis.ts` in your App: +To use it, you'll first need to register the `FeatureFlags` API via `ApiRegistry` in your `apis.ts` in your App: ```tsx import { @@ -34,8 +48,12 @@ import { Button } from '@material-ui/core'; import { featureFlagsApiRef, useApi } from '@backstage/core'; const ExampleButton: FC<{}> = () => { - const featureFlagsApi = useApi(featureFlagsApiRef); - const handleClick = () => featureFlagsApi.set('enable-example-feature', .Enabled); + const { useFeatureFlag } = useApi(featureFlagsApiRef); + const [flagState, setFlagState] = useFeatureFlag('enable-example-feature'); + + const handleClick = () => { + setFlagState(FeatureFlagState.Enabled); + }; return ( - ); -}; -``` +Note that you must register it with `registerFeatureFlag` otherwise the `useFeatureFlag` will throw an error. [Back to References](README.md) diff --git a/packages/core/src/api/app/FeatureFlags.test.tsx b/packages/core/src/api/app/FeatureFlags.test.tsx index 3e3bb3e50d..b803b27b3a 100644 --- a/packages/core/src/api/app/FeatureFlags.test.tsx +++ b/packages/core/src/api/app/FeatureFlags.test.tsx @@ -14,142 +14,362 @@ * limitations under the License. */ -import React, { useContext } from 'react'; +import React, { ReactNode, useContext, useEffect, useRef } from 'react'; import { render } from '@testing-library/react'; import { FeatureFlags, + FeatureFlagsEntry, FeatureFlagsContext, FeatureFlagsContextProvider, } from './FeatureFlags'; import { FeatureFlagState } from '../apis/definitions/featureFlags'; +function useRenderCount() { + const renderCount = useRef(-1); + renderCount.current += 1; + return renderCount.current; +} + +function withFeatureFlags( + children: ReactNode, + featureFlags: Set = new Set([ + { name: 'feature-flag-one', pluginId: 'plugin-one' }, + { name: 'feature-flag-two', pluginId: 'plugin-two' }, + { name: 'feature-flag-three', pluginId: 'plugin-two' }, + ]), +) { + return ( + + {children} + + ); +} + describe('FeatureFlags', () => { beforeEach(() => { window.localStorage.clear(); }); - describe('#get', () => { - it('defaults to .NotEnabled', () => { - expect(FeatureFlags.get('enable-feature-flag')).toBe( - FeatureFlagState.NotEnabled, - ); + describe('#getEnabledFeatureFlags', () => { + it('returns an empty set', () => { + expect(FeatureFlags.getEnabledFeatureFlags()).toEqual(new Set()); }); - it('returns a .Enabled state', () => { - FeatureFlags.set('enable-feature-flag', FeatureFlagState.Enabled); - expect(FeatureFlags.get('enable-feature-flag')).toBe( - FeatureFlagState.Enabled, + it('returns enabled feature flags', () => { + window.localStorage.setItem( + 'featureFlags', + JSON.stringify({ + 'feature-flag-one': true, + 'feature-flag-three': true, + }), ); - }); - it('returns a .NotEnabled state', () => { - FeatureFlags.set('enable-feature-flag', FeatureFlagState.NotEnabled); - expect(FeatureFlags.get('enable-feature-flag')).toBe( - FeatureFlagState.NotEnabled, + expect(FeatureFlags.getEnabledFeatureFlags()).toEqual( + new Set(['feature-flag-one', 'feature-flag-three']), ); }); }); - describe('#set', () => { - it('fails if name is less than three characters', () => { - expect(() => { - FeatureFlags.set('ab', FeatureFlagState.NotEnabled); - }).toThrow(/minimum length of three characters/i); + describe('#checkFeatureFlagNameErrors', () => { + it('returns an error if less than three characters', () => { + const errors = FeatureFlags.checkFeatureFlagNameErrors('ab'); + expect(errors[0]).toMatch(/minimum length of three characters/i); }); - it('fails if name is greater than 150 characters', () => { - expect(() => { - FeatureFlags.set( - 'loremipsumdolorsitametconsecteturadipiscingelitnuncvitaeportaexaullamcorperturpismaurisutmattisnequemorbisediaculisauguevivamuspulvinarcursuseratblandithendreritquisqueuttinciduntmagnavestibulumblanditaugueat', - FeatureFlagState.NotEnabled, + it('returns an error if greater than 150 characters', () => { + const errors = FeatureFlags.checkFeatureFlagNameErrors( + 'loremipsumdolorsitametconsecteturadipiscingelitnuncvitaeportaexaullamcorperturpismaurisutmattisnequemorbisediaculisauguevivamuspulvinarcursuseratblandithendreritquisqueuttinciduntmagnavestibulumblanditaugueat', + ); + expect(errors[0]).toMatch(/not exceed 150 characters/i); + }); + + it('returns an error if name does not start with a lowercase letter', () => { + const errors = FeatureFlags.checkFeatureFlagNameErrors('123456789'); + expect(errors[0]).toMatch(/start with a lowercase letter/i); + }); + + it('returns an error if name contains characters other than lowercase letters, numbers and hyphens', () => { + const errors = FeatureFlags.checkFeatureFlagNameErrors( + 'Invalid_Feature_Flag', + ); + expect(errors[0]).toMatch( + /only contain lowercase letters, numbers and hyphens/i, + ); + }); + + it('returns no errors', () => { + const errors = FeatureFlags.checkFeatureFlagNameErrors( + 'valid-feature-flag', + ); + expect(errors.length).toBe(0); + }); + }); + + describe('#useFeatureFlag', () => { + it('throws an error if the feature flag is not registered', () => { + const Component = () => { + expect(() => { + FeatureFlags.useFeatureFlag('feature-flag-four'); + }).toThrow(/'feature-flag-four' feature flag is not registered/i); + + return null; + }; + + render(withFeatureFlags()); + }); + + it('throws an error if changing value is not recognized', () => { + const Component = () => { + const [, setState] = FeatureFlags.useFeatureFlag('feature-flag-one'); + const renderCount = useRenderCount(); + if (renderCount === 1) { + // @ts-ignore + expect(() => setState('not valid')).toThrow( + /requires a recognized value from the FeatureFlagState/i, + ); + } + return null; + }; + + render(withFeatureFlags()); + }); + + it('defaults to .NotEnabled', () => { + const Component = () => { + const renderCount = useRenderCount(); + const [state] = FeatureFlags.useFeatureFlag('feature-flag-one'); + if (renderCount === 1) { + expect(state).toEqual(FeatureFlagState.NotEnabled); + } + return null; + }; + + render(withFeatureFlags()); + }); + + it('returns an .Enabled state', () => { + window.localStorage.setItem('featureFlags', '{"feature-flag-one":true}'); + + const Component = () => { + const [state] = FeatureFlags.useFeatureFlag('feature-flag-one'); + const renderCount = useRenderCount(); + if (renderCount === 1) { + expect(state).toEqual(FeatureFlagState.Enabled); + } + return null; + }; + + render(withFeatureFlags()); + }); + + it('returns an .NotEnabled state', () => { + const Component = () => { + const [state] = FeatureFlags.useFeatureFlag('feature-flag-one'); + const renderCount = useRenderCount(); + if (renderCount === 1) { + expect(state).toEqual(FeatureFlagState.NotEnabled); + } + return null; + }; + + render(withFeatureFlags()); + }); + + it('changes state to .Enabled', () => { + const Component = () => { + const [state, setState] = FeatureFlags.useFeatureFlag( + 'feature-flag-one', ); - }).toThrow(/not exceed 150 characters/i); + const renderCount = useRenderCount(); + if (renderCount === 1) setState(FeatureFlagState.Enabled); + if (renderCount === 2) expect(state).toEqual(FeatureFlagState.Enabled); + return null; + }; + + render(withFeatureFlags()); }); - it('fails if name does not start with a lowercase letter', () => { - expect(() => { - FeatureFlags.set('123456789', FeatureFlagState.NotEnabled); - }).toThrow(/start with a lowercase letter/i); + it('changes state to .NotEnabled', () => { + window.localStorage.setItem('featureFlags', '{"feature-flag-one":true}'); + + const Component = () => { + const [state, setState] = FeatureFlags.useFeatureFlag( + 'feature-flag-one', + ); + const renderCount = useRenderCount(); + if (renderCount === 1) setState(FeatureFlagState.NotEnabled); + if (renderCount === 2) + expect(state).toEqual(FeatureFlagState.NotEnabled); + return null; + }; + + render(withFeatureFlags()); }); - it('fails if name contains characters other than lowercase letters, numbers and hyphens', () => { - expect(() => { - FeatureFlags.set('Invalid_Feature_Flag', FeatureFlagState.NotEnabled); - }).toThrow(/only contain lowercase letters, numbers and hyphens/i); + it('changes state to .Enabled then .NotEnabled', () => { + const Component = () => { + const [state, setState] = FeatureFlags.useFeatureFlag( + 'feature-flag-one', + ); + const renderCount = useRenderCount(); + if (renderCount === 1) setState(FeatureFlagState.Enabled); + if (renderCount === 2) setState(FeatureFlagState.NotEnabled); + if (renderCount === 3) + expect(state).toEqual(FeatureFlagState.NotEnabled); + return null; + }; + + render(withFeatureFlags()); }); - it('fails if state is not recognized from FeatureFlagState', () => { - expect(() => { - // @ts-ignore - FeatureFlags.set('valid-feature-flag', 'invalid state'); - }).toThrow(/requires a recognized value from the FeatureFlagState/i); - }); + it('changes multiple feature flag states', () => { + expect.assertions(3); - it('sets a feature flag to enabled', () => { - expect(window.localStorage.featureFlags).toBeUndefined(); - FeatureFlags.set('valid-feature-flag', FeatureFlagState.Enabled); - expect(window.localStorage.featureFlags).toBe( - '{"valid-feature-flag":true}', - ); - }); + const Component = () => { + const renderCount = useRenderCount(); + const [stateA, setStateA] = FeatureFlags.useFeatureFlag( + 'feature-flag-one', + ); + const [stateB, setStateB] = FeatureFlags.useFeatureFlag( + 'feature-flag-two', + ); - it('sets a feature flag to disabled', () => { - expect(window.localStorage.featureFlags).toBeUndefined(); - FeatureFlags.set('valid-feature-flag', FeatureFlagState.NotEnabled); - expect(window.localStorage.featureFlags).toBe('{}'); - }); + useEffect(() => { + if (renderCount === 1) { + setStateA(FeatureFlagState.Enabled); + setStateB(FeatureFlagState.Enabled); + } + if (renderCount === 2) { + expect(stateA).toEqual(FeatureFlagState.Enabled); + expect(stateB).toEqual(FeatureFlagState.Enabled); + expect(window.localStorage.getItem('featureFlags')).toEqual( + '{"feature-flag-one":true,"feature-flag-two":true}', + ); + } + }, [renderCount]); - it('sets a feature flag to disabled then enabled', () => { - expect(window.localStorage.featureFlags).toBeUndefined(); - FeatureFlags.set('valid-feature-flag', FeatureFlagState.NotEnabled); - FeatureFlags.set('valid-feature-flag', FeatureFlagState.Enabled); - expect(window.localStorage.featureFlags).toBe( - '{"valid-feature-flag":true}', - ); - }); + return null; + }; - it('sets multiple feature flags', () => { - expect(window.localStorage.featureFlags).toBeUndefined(); - FeatureFlags.set('valid-feature-flag', FeatureFlagState.Enabled); - FeatureFlags.set('another-valid-feature-flag', FeatureFlagState.Enabled); - expect(window.localStorage.featureFlags).toBe( - '{"valid-feature-flag":true,"another-valid-feature-flag":true}', - ); + render(withFeatureFlags()); }); }); }); describe('FeatureFlagsContext', () => { - it('returns an empty list without the context', () => { - expect.assertions(1); + beforeEach(() => { + window.localStorage.clear(); + }); + it('returns an empty set without the context', () => { const Component = () => { const { featureFlags } = useContext(FeatureFlagsContext); - expect(featureFlags).toEqual([]); + expect(featureFlags).toEqual(new Set()); return null; }; render(); }); - it('returns a list of registered feature flags', () => { - expect.assertions(1); + it('returns a set of registered feature flags', () => { + expect.assertions(2); - const mockFeatureFlags = [ + const mockFeatureFlags = new Set([ { name: 'feature-flag-one', pluginId: 'plugin-one' }, { name: 'feature-flag-two', pluginId: 'plugin-two' }, - ]; + ]); const Component = () => { const { featureFlags } = useContext(FeatureFlagsContext); - expect(featureFlags).toBe(mockFeatureFlags); + expect(featureFlags).toEqual(mockFeatureFlags); return null; }; - render( - - - , + render(withFeatureFlags(, mockFeatureFlags)); + }); + + it('returns a set of user enabled feature flags', () => { + expect.assertions(1); + + window.localStorage.setItem( + 'featureFlags', + JSON.stringify({ + 'feature-flag-one': true, + 'feature-flag-three': true, + }), ); + + const Component = () => { + const { enabledFeatureFlags } = useContext(FeatureFlagsContext); + + if (enabledFeatureFlags.size > 0) { + expect(enabledFeatureFlags).toEqual( + new Set(['feature-flag-one', 'feature-flag-three']), + ); + } + + return null; + }; + + render(withFeatureFlags()); + }); + + it('correctly re-renders when calling refreshEnabledFeatureFlags', () => { + // First is the initial context + // Second is the context with the state from FeatureFlagsContextProvider + // Third is from calling refreshEnabledFeatureFlags + expect.assertions(3); + + const FirstRender = ({ context: { enabledFeatureFlags } }) => { + useEffect(() => { + expect(enabledFeatureFlags).toEqual(new Set()); + }, []); + return null; + }; + + const SecondRender = ({ + context: { enabledFeatureFlags, refreshEnabledFeatureFlags }, + }) => { + useEffect(() => { + expect(enabledFeatureFlags).toEqual(new Set()); + + // Change localStorage + window.localStorage.setItem( + 'featureFlags', + JSON.stringify({ + 'feature-flag-one': true, + 'feature-flag-three': true, + }), + ); + + // Refresh + refreshEnabledFeatureFlags(); + }, []); + + return null; + }; + + const ThirdRender = ({ context: { enabledFeatureFlags } }) => { + useEffect(() => { + expect(enabledFeatureFlags).toEqual( + new Set(['feature-flag-one', 'feature-flag-three']), + ); + }, []); + + return null; + }; + + const Component = () => { + const context = useContext(FeatureFlagsContext); + const renderCount = useRenderCount(); + + if (renderCount === 0) return ; + if (renderCount === 1) return ; + if (renderCount === 2) return ; + + return null; + }; + + render(withFeatureFlags()); }); }); diff --git a/packages/core/src/api/app/FeatureFlags.tsx b/packages/core/src/api/app/FeatureFlags.tsx index e5a2063621..4f2b7da7d0 100644 --- a/packages/core/src/api/app/FeatureFlags.tsx +++ b/packages/core/src/api/app/FeatureFlags.tsx @@ -156,21 +156,19 @@ class FeatureFlagsImpl implements FeatureFlagsApi { // We don't make this private as we need this to validate // in the `registerFeatureFlag` method in the Plugin API. checkFeatureFlagNameErrors(name: FeatureFlagName): string[] { - const errors = []; + const errors: string[] = []; if (name.length < 3) { - errors.push( - 'The `name` argument must have a minimum length of three characters.', - ); + errors.push('The name must have a minimum length of three characters.'); } if (name.length > 150) { - errors.push('The `name` argument must not exceed 150 characters.'); + errors.push('The name must not exceed 150 characters.'); } if (!name.match(/^[a-z]+[a-z0-9-]+$/)) { errors.push( - 'The `name` argument must start with a lowercase letter and only contain lowercase letters, numbers and hyphens. ' + + 'The name must start with a lowercase letter and only contain lowercase letters, numbers and hyphens. ' + 'Examples: feature-flag-one, alpha, release-2020', ); } From 6fa6373536c00d4375f93c89fe0b57367e637756 Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Fri, 27 Mar 2020 18:53:54 +0100 Subject: [PATCH 095/146] [core/apis] replaced FeatureFlagsContext with FeatureFlags.registeredFlags --- packages/core/src/api/app/AppBuilder.tsx | 11 +- .../core/src/api/app/FeatureFlags.test.tsx | 133 +----------------- packages/core/src/api/app/FeatureFlags.tsx | 102 +------------- packages/core/src/api/index.ts | 6 +- 4 files changed, 8 insertions(+), 244 deletions(-) diff --git a/packages/core/src/api/app/AppBuilder.tsx b/packages/core/src/api/app/AppBuilder.tsx index bd40565260..6964c45a26 100644 --- a/packages/core/src/api/app/AppBuilder.tsx +++ b/packages/core/src/api/app/AppBuilder.tsx @@ -19,7 +19,7 @@ import { Route, Switch, Redirect } from 'react-router-dom'; import { AppContextProvider } from './AppContext'; import { App } from './types'; import BackstagePlugin from '../plugin/Plugin'; -import { FeatureFlagsEntry, FeatureFlagsContextProvider } from './FeatureFlags'; +import { FeatureFlags, FeatureFlagsEntry } from './FeatureFlags'; import { IconComponent, SystemIcons, @@ -102,6 +102,8 @@ export default class AppBuilder { } } + FeatureFlags.registeredFeatureFlags = registeredFeatureFlags; + routes.push( , ); @@ -117,13 +119,6 @@ export default class AppBuilder { rendered = ; } - rendered = ( - - ); - return () => ; } } diff --git a/packages/core/src/api/app/FeatureFlags.test.tsx b/packages/core/src/api/app/FeatureFlags.test.tsx index b803b27b3a..cb5be32394 100644 --- a/packages/core/src/api/app/FeatureFlags.test.tsx +++ b/packages/core/src/api/app/FeatureFlags.test.tsx @@ -16,12 +16,7 @@ import React, { ReactNode, useContext, useEffect, useRef } from 'react'; import { render } from '@testing-library/react'; -import { - FeatureFlags, - FeatureFlagsEntry, - FeatureFlagsContext, - FeatureFlagsContextProvider, -} from './FeatureFlags'; +import { FeatureFlags } from './FeatureFlags'; import { FeatureFlagState } from '../apis/definitions/featureFlags'; function useRenderCount() { @@ -37,13 +32,7 @@ function withFeatureFlags( { name: 'feature-flag-two', pluginId: 'plugin-two' }, { name: 'feature-flag-three', pluginId: 'plugin-two' }, ]), -) { - return ( - - {children} - - ); -} +) {} describe('FeatureFlags', () => { beforeEach(() => { @@ -255,121 +244,3 @@ describe('FeatureFlags', () => { }); }); }); - -describe('FeatureFlagsContext', () => { - beforeEach(() => { - window.localStorage.clear(); - }); - - it('returns an empty set without the context', () => { - const Component = () => { - const { featureFlags } = useContext(FeatureFlagsContext); - expect(featureFlags).toEqual(new Set()); - return null; - }; - - render(); - }); - - it('returns a set of registered feature flags', () => { - expect.assertions(2); - - const mockFeatureFlags = new Set([ - { name: 'feature-flag-one', pluginId: 'plugin-one' }, - { name: 'feature-flag-two', pluginId: 'plugin-two' }, - ]); - - const Component = () => { - const { featureFlags } = useContext(FeatureFlagsContext); - expect(featureFlags).toEqual(mockFeatureFlags); - return null; - }; - - render(withFeatureFlags(, mockFeatureFlags)); - }); - - it('returns a set of user enabled feature flags', () => { - expect.assertions(1); - - window.localStorage.setItem( - 'featureFlags', - JSON.stringify({ - 'feature-flag-one': true, - 'feature-flag-three': true, - }), - ); - - const Component = () => { - const { enabledFeatureFlags } = useContext(FeatureFlagsContext); - - if (enabledFeatureFlags.size > 0) { - expect(enabledFeatureFlags).toEqual( - new Set(['feature-flag-one', 'feature-flag-three']), - ); - } - - return null; - }; - - render(withFeatureFlags()); - }); - - it('correctly re-renders when calling refreshEnabledFeatureFlags', () => { - // First is the initial context - // Second is the context with the state from FeatureFlagsContextProvider - // Third is from calling refreshEnabledFeatureFlags - expect.assertions(3); - - const FirstRender = ({ context: { enabledFeatureFlags } }) => { - useEffect(() => { - expect(enabledFeatureFlags).toEqual(new Set()); - }, []); - return null; - }; - - const SecondRender = ({ - context: { enabledFeatureFlags, refreshEnabledFeatureFlags }, - }) => { - useEffect(() => { - expect(enabledFeatureFlags).toEqual(new Set()); - - // Change localStorage - window.localStorage.setItem( - 'featureFlags', - JSON.stringify({ - 'feature-flag-one': true, - 'feature-flag-three': true, - }), - ); - - // Refresh - refreshEnabledFeatureFlags(); - }, []); - - return null; - }; - - const ThirdRender = ({ context: { enabledFeatureFlags } }) => { - useEffect(() => { - expect(enabledFeatureFlags).toEqual( - new Set(['feature-flag-one', 'feature-flag-three']), - ); - }, []); - - return null; - }; - - const Component = () => { - const context = useContext(FeatureFlagsContext); - const renderCount = useRenderCount(); - - if (renderCount === 0) return ; - if (renderCount === 1) return ; - if (renderCount === 2) return ; - - return null; - }; - - render(withFeatureFlags()); - }); -}); diff --git a/packages/core/src/api/app/FeatureFlags.tsx b/packages/core/src/api/app/FeatureFlags.tsx index 4f2b7da7d0..172daeb878 100644 --- a/packages/core/src/api/app/FeatureFlags.tsx +++ b/packages/core/src/api/app/FeatureFlags.tsx @@ -14,78 +14,17 @@ * limitations under the License. */ -import React, { - ReactNode, - createContext, - useContext, - useState, - useEffect, - FC, -} from 'react'; import { FeatureFlagName } from '../plugin/types'; import { FeatureFlagState, FeatureFlagsApi, } from '../apis/definitions/featureFlags'; -/** - * Create a shared React context for Feature Flags. - * - * This will be used to propagate all available feature flags to - * Backstage components. This enables viewing all of the available flags. - */ export interface FeatureFlagsEntry { pluginId: string; name: FeatureFlagName; } -export interface IFeatureFlagsContext { - featureFlags: Set; - enabledFeatureFlags: Set; - refreshEnabledFeatureFlags: () => void; -} - -export const FeatureFlagsContext = createContext({ - featureFlags: new Set(), - enabledFeatureFlags: new Set(), - refreshEnabledFeatureFlags: () => { - throw new Error( - 'The refreshEnabledFeatureFlags method is not implemented as it is not called from within the FeatureFlagsContext context within React. ' + - 'See the Backstage documentation for examples on how to use the Feature Flags API.', - ); - }, -}); - -export const FeatureFlagsContextProvider: FC<{ - featureFlags: Set; - children: ReactNode; -}> = ({ featureFlags, children }) => { - const [enabledFeatureFlags, setEnabledFeatureFlags] = useState< - Set - >(new Set()); - - const refreshEnabledFeatureFlags = () => { - // eslint-disable-next-line no-use-before-define - setEnabledFeatureFlags(FeatureFlags.getEnabledFeatureFlags()); - }; - - // Initially populate our setEnabledFeatureFlags - useEffect(() => { - refreshEnabledFeatureFlags(); - }, []); - - return ( - - ); -}; - /** * Create the FeatureFlags implementation based on the API. */ @@ -95,46 +34,9 @@ export const FeatureFlagsContextProvider: FC<{ class FeatureFlagsImpl implements FeatureFlagsApi { private readonly localStorageKey = 'featureFlags'; - private get( - enabledFeatureFlags: Set, - name: FeatureFlagName, - ): FeatureFlagState { - if (enabledFeatureFlags.has(name)) { - return FeatureFlagState.Enabled; - } + public constructor(public registeredFeatureFlags: FeatureFlagsEntry[] = []) {} - return FeatureFlagState.NotEnabled; - } - - private set(name: FeatureFlagName, state: FeatureFlagState): void { - const errors = this.checkFeatureFlagNameErrors(name); - const flags = this.getEnabledFeatureFlags(); - - if (errors.length > 0) { - throw new Error(errors[0]); - } - - if (state === FeatureFlagState.Enabled) { - flags.add(name); - } else if (state === FeatureFlagState.NotEnabled) { - flags.delete(name); - } else { - throw new Error( - 'The `state` argument requires a recognized value from the FeatureFlagState enum. ' + - 'Please check the Backstage documentation to see all the available options.' + - 'Example values: FeatureFlagState.NotEnabled, FeatureFlagState.Enabled', - ); - } - - window.localStorage.setItem( - this.localStorageKey, - JSON.stringify( - [...flags].reduce((list, flag) => ({ ...list, [flag]: true }), {}), - ), - ); - } - - getEnabledFeatureFlags(): Set { + private getUserEnabledFeatureFlags(): Set { if (!('localStorage' in window)) { throw new Error( 'Feature Flags are not supported on browsers without the Local Storage API', diff --git a/packages/core/src/api/index.ts b/packages/core/src/api/index.ts index 4c77e94984..e60a7efa18 100644 --- a/packages/core/src/api/index.ts +++ b/packages/core/src/api/index.ts @@ -16,9 +16,5 @@ export * from './api'; export * from './apis'; -export { - FeatureFlags, - FeatureFlagsContext, - FeatureFlagsContextProvider, -} from './app/FeatureFlags'; +export { FeatureFlags } from './app/FeatureFlags'; export { useApp } from './app/AppContext'; From 64a14b2191e3d94dbaa3a7dd406b9c55605807e7 Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Fri, 27 Mar 2020 19:45:59 +0100 Subject: [PATCH 096/146] [core/apis] adapted to build on existing data structures --- docs/reference/createPlugin-feature-flags.md | 11 +- docs/reference/createPlugin.md | 2 +- packages/app/src/apis.ts | 4 +- .../src/api/apis/definitions/featureFlags.ts | 30 +- packages/core/src/api/app/AppBuilder.tsx | 12 +- .../core/src/api/app/FeatureFlags.test.tsx | 372 +++++++++--------- packages/core/src/api/app/FeatureFlags.tsx | 233 ++++++----- packages/core/src/api/plugin/Plugin.tsx | 17 +- .../ToggleFeatureFlagButton.test.tsx | 38 +- .../WelcomePage/ToggleFeatureFlagButton.tsx | 22 +- plugins/welcome/src/plugin.ts | 2 +- 11 files changed, 387 insertions(+), 356 deletions(-) diff --git a/docs/reference/createPlugin-feature-flags.md b/docs/reference/createPlugin-feature-flags.md index bbf7f17c32..6006dc6cb7 100644 --- a/docs/reference/createPlugin-feature-flags.md +++ b/docs/reference/createPlugin-feature-flags.md @@ -4,7 +4,7 @@ The `featureFlags` object passed to the `register` function makes it possible fo ```typescript export type FeatureFlagsHooks = { - registerFeatureFlag(name: FeatureFlagName): void; + register(name: FeatureFlagName): void; }; ``` @@ -17,7 +17,7 @@ export default createPlugin({ id: 'welcome', register({ router, featureFlags }) { // router.registerRoute('/', Component); - featureFlags.registerFeatureFlag('enable-example-feature'); + featureFlags.register('enable-example-feature'); }, }); ``` @@ -48,11 +48,10 @@ import { Button } from '@material-ui/core'; import { featureFlagsApiRef, useApi } from '@backstage/core'; const ExampleButton: FC<{}> = () => { - const { useFeatureFlag } = useApi(featureFlagsApiRef); - const [flagState, setFlagState] = useFeatureFlag('enable-example-feature'); + const flags = useApi(featureFlagsApiRef).getFlags(); const handleClick = () => { - setFlagState(FeatureFlagState.Enabled); + flags.set('enable-example-feature', FeatureFlagState.Enabled); }; return ( @@ -63,6 +62,4 @@ const ExampleButton: FC<{}> = () => { }; ``` -Note that you must register it with `registerFeatureFlag` otherwise the `useFeatureFlag` will throw an error. - [Back to References](README.md) diff --git a/docs/reference/createPlugin.md b/docs/reference/createPlugin.md index b19df50c22..de4636e11d 100644 --- a/docs/reference/createPlugin.md +++ b/docs/reference/createPlugin.md @@ -33,7 +33,7 @@ import ExampleComponent from './components/ExampleComponent'; export default createPlugin({ id: 'new-plugin', register({ router, featureFlags }) { - featureFlags.registerFeatureFlag('enable-example-component'); + featureFlags.register('enable-example-component'); router.registerRoute('/new-plugin', ExampleComponent); }, diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index 4d339c3734..494b469312 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -26,8 +26,8 @@ import { ErrorDisplayForwarder } from './components/ErrorDisplay/ErrorDisplay'; const builder = ApiRegistry.builder(); export const errorDialogForwarder = new ErrorDisplayForwarder(); - builder.add(errorApiRef, errorDialogForwarder); -builder.add(featureFlagsApiRef, FeatureFlags); + +builder.add(featureFlagsApiRef, new FeatureFlags()); export default builder.build() as ApiHolder; diff --git a/packages/core/src/api/apis/definitions/featureFlags.ts b/packages/core/src/api/apis/definitions/featureFlags.ts index e3eaeefdfb..95fd02a1cb 100644 --- a/packages/core/src/api/apis/definitions/featureFlags.ts +++ b/packages/core/src/api/apis/definitions/featureFlags.ts @@ -15,7 +15,11 @@ */ import ApiRef from '../ApiRef'; -import { FeatureFlagName } from '../../plugin/types'; +import { + UserFlags, + FeatureFlagsRegistry, + FeatureFlagsRegistryItem, +} from '../../app/FeatureFlags'; /** * The feature flags API is used to toggle functionality to users across plugins and Backstage. @@ -34,30 +38,22 @@ export enum FeatureFlagState { Enabled = 1, } -export type FeatureFlagsApi = { +export interface FeatureFlagsApi { /** - * Get a list of the user's currently enabled feature flags. - * Reads directly from window.localStorage Browser API. - * - * @returns string[] enabledFeatureFlags List of feature flags enabled by the current user + * Store a list of registered feature flags. */ - getEnabledFeatureFlags(): Set; + registeredFeatureFlags: FeatureFlagsRegistryItem[]; /** - * Check the feature flag name convention. - * Used in the `registerFeatureFlag` method as well as in the `set` method. - * - * @returns string[] errors List of errors as string. Empty array if no errors. + * Get a list of all feature flags from the current user. */ - checkFeatureFlagNameErrors(name: FeatureFlagName): string[]; + getFlags(): UserFlags; /** - * Use Feature Flags as a React Hook. + * Get a list of all registered flags. */ - useFeatureFlag( - name: FeatureFlagName, - ): [FeatureFlagState, (state: FeatureFlagState) => void]; -}; + getRegisteredFlags(): FeatureFlagsRegistry; +} export const featureFlagsApiRef = new ApiRef({ id: 'core.featureflags', diff --git a/packages/core/src/api/app/AppBuilder.tsx b/packages/core/src/api/app/AppBuilder.tsx index 6964c45a26..9aca19b1fe 100644 --- a/packages/core/src/api/app/AppBuilder.tsx +++ b/packages/core/src/api/app/AppBuilder.tsx @@ -19,7 +19,8 @@ import { Route, Switch, Redirect } from 'react-router-dom'; import { AppContextProvider } from './AppContext'; import { App } from './types'; import BackstagePlugin from '../plugin/Plugin'; -import { FeatureFlags, FeatureFlagsEntry } from './FeatureFlags'; +import { FeatureFlagsRegistryItem } from './FeatureFlags'; +import { featureFlagsApiRef } from '../apis/definitions/featureFlags'; import { IconComponent, SystemIcons, @@ -63,7 +64,7 @@ export default class AppBuilder { const app = new AppImpl(this.systemIcons); const routes = new Array(); - const registeredFeatureFlags = new Set(); + const registeredFeatureFlags = new Array(); for (const plugin of this.plugins.values()) { for (const output of plugin.output()) { @@ -90,7 +91,7 @@ export default class AppBuilder { break; } case 'feature-flag': { - registeredFeatureFlags.add({ + registeredFeatureFlags.push({ pluginId: plugin.getId(), name: output.name, }); @@ -102,7 +103,10 @@ export default class AppBuilder { } } - FeatureFlags.registeredFeatureFlags = registeredFeatureFlags; + const FeatureFlags = this.apis && this.apis.get(featureFlagsApiRef); + if (FeatureFlags) { + FeatureFlags!.registeredFeatureFlags = registeredFeatureFlags; + } routes.push( , diff --git a/packages/core/src/api/app/FeatureFlags.test.tsx b/packages/core/src/api/app/FeatureFlags.test.tsx index cb5be32394..1ad247634c 100644 --- a/packages/core/src/api/app/FeatureFlags.test.tsx +++ b/packages/core/src/api/app/FeatureFlags.test.tsx @@ -14,233 +14,231 @@ * limitations under the License. */ -import React, { ReactNode, useContext, useEffect, useRef } from 'react'; -import { render } from '@testing-library/react'; -import { FeatureFlags } from './FeatureFlags'; +import { FeatureFlags as FeatureFlagsImpl } from './FeatureFlags'; import { FeatureFlagState } from '../apis/definitions/featureFlags'; -function useRenderCount() { - const renderCount = useRef(-1); - renderCount.current += 1; - return renderCount.current; -} - -function withFeatureFlags( - children: ReactNode, - featureFlags: Set = new Set([ - { name: 'feature-flag-one', pluginId: 'plugin-one' }, - { name: 'feature-flag-two', pluginId: 'plugin-two' }, - { name: 'feature-flag-three', pluginId: 'plugin-two' }, - ]), -) {} - describe('FeatureFlags', () => { beforeEach(() => { window.localStorage.clear(); }); - describe('#getEnabledFeatureFlags', () => { - it('returns an empty set', () => { - expect(FeatureFlags.getEnabledFeatureFlags()).toEqual(new Set()); + describe('#getFlags', () => { + let FeatureFlags; + + beforeEach(() => { + FeatureFlags = new FeatureFlagsImpl(); }); - it('returns enabled feature flags', () => { + it('returns no flags', () => { + expect(FeatureFlags.getFlags().toObject()).toMatchObject({}); + }); + + it('returns the correct flags', () => { window.localStorage.setItem( 'featureFlags', JSON.stringify({ - 'feature-flag-one': true, - 'feature-flag-three': true, + 'feature-flag-one': 1, + 'feature-flag-two': 1, + 'feature-flag-three': 0, }), ); - expect(FeatureFlags.getEnabledFeatureFlags()).toEqual( - new Set(['feature-flag-one', 'feature-flag-three']), + expect(FeatureFlags.getFlags().toObject()).toMatchObject({ + 'feature-flag-one': FeatureFlagState.Enabled, + 'feature-flag-two': FeatureFlagState.Enabled, + 'feature-flag-three': FeatureFlagState.NotEnabled, + }); + }); + + it('gets the correct values', () => { + window.localStorage.setItem( + 'featureFlags', + JSON.stringify({ + 'feature-flag-one': 1, + 'feature-flag-two': 0, + }), ); + + expect(FeatureFlags.getFlags().get('feature-flag-one')).toEqual( + FeatureFlagState.Enabled, + ); + expect(FeatureFlags.getFlags().get('feature-flag-two')).toEqual( + FeatureFlagState.NotEnabled, + ); + expect(FeatureFlags.getFlags().get('feature-flag-three')).toEqual( + FeatureFlagState.NotEnabled, + ); + }); + + it('sets the correct values', () => { + const flags = FeatureFlags.getFlags(); + flags.set('feature-flag-zero', FeatureFlagState.Enabled); + + expect(flags.get('feature-flag-zero')).toEqual(FeatureFlagState.Enabled); + expect(window.localStorage.getItem('featureFlags')).toEqual( + '{"feature-flag-zero":1}', + ); + }); + + it('deletes the correct values', () => { + window.localStorage.setItem( + 'featureFlags', + JSON.stringify({ + 'feature-flag-one': 1, + 'feature-flag-two': 0, + }), + ); + + const flags = FeatureFlags.getFlags(); + flags.delete('feature-flag-one'); + + expect(flags.get('feature-flag-one')).toEqual( + FeatureFlagState.NotEnabled, + ); + expect(window.localStorage.getItem('featureFlags')).toEqual( + '{"feature-flag-two":0}', + ); + }); + + it('clears all values', () => { + window.localStorage.setItem( + 'featureFlags', + JSON.stringify({ + 'feature-flag-one': 1, + 'feature-flag-two': 1, + 'feature-flag-three': 0, + }), + ); + + const flags = FeatureFlags.getFlags(); + flags.clear(); + + expect(flags.toObject()).toEqual({}); + expect(window.localStorage.getItem('featureFlags')).toEqual('{}'); }); }); - describe('#checkFeatureFlagNameErrors', () => { - it('returns an error if less than three characters', () => { - const errors = FeatureFlags.checkFeatureFlagNameErrors('ab'); - expect(errors[0]).toMatch(/minimum length of three characters/i); + describe('#getRegisteredFlags', () => { + let FeatureFlags; + + beforeEach(() => { + FeatureFlags = new FeatureFlagsImpl(); + FeatureFlags.registeredFeatureFlags = [ + { name: 'registered-flag-1', pluginId: 'plugin-one' }, + { name: 'registered-flag-2', pluginId: 'plugin-one' }, + { name: 'registered-flag-3', pluginId: 'plugin-two' }, + ]; }); - it('returns an error if greater than 150 characters', () => { - const errors = FeatureFlags.checkFeatureFlagNameErrors( - 'loremipsumdolorsitametconsecteturadipiscingelitnuncvitaeportaexaullamcorperturpismaurisutmattisnequemorbisediaculisauguevivamuspulvinarcursuseratblandithendreritquisqueuttinciduntmagnavestibulumblanditaugueat', - ); - expect(errors[0]).toMatch(/not exceed 150 characters/i); + it('should return an empty list', () => { + FeatureFlags.registeredFeatureFlags = []; + expect(FeatureFlags.getRegisteredFlags().toObject()).toEqual([]); }); - it('returns an error if name does not start with a lowercase letter', () => { - const errors = FeatureFlags.checkFeatureFlagNameErrors('123456789'); - expect(errors[0]).toMatch(/start with a lowercase letter/i); + it('should return an valid list', () => { + expect(FeatureFlags.getRegisteredFlags().toObject()).toMatchObject([ + { name: 'registered-flag-1', pluginId: 'plugin-one' }, + { name: 'registered-flag-2', pluginId: 'plugin-one' }, + { name: 'registered-flag-3', pluginId: 'plugin-two' }, + ]); }); - it('returns an error if name contains characters other than lowercase letters, numbers and hyphens', () => { - const errors = FeatureFlags.checkFeatureFlagNameErrors( - 'Invalid_Feature_Flag', - ); - expect(errors[0]).toMatch( - /only contain lowercase letters, numbers and hyphens/i, - ); + it('should get the correct values', () => { + const getByName = name => + FeatureFlags.getRegisteredFlags().find(flag => flag.name === name); + + expect(getByName('registered-flag-0')).toBeUndefined(); + expect(getByName('registered-flag-1')).toEqual({ + name: 'registered-flag-1', + pluginId: 'plugin-one', + }); + expect(getByName('registered-flag-2')).toEqual({ + name: 'registered-flag-2', + pluginId: 'plugin-one', + }); + expect(getByName('registered-flag-3')).toEqual({ + name: 'registered-flag-3', + pluginId: 'plugin-two', + }); }); - it('returns no errors', () => { - const errors = FeatureFlags.checkFeatureFlagNameErrors( - 'valid-feature-flag', - ); - expect(errors.length).toBe(0); - }); - }); + it('should append the correct value', () => { + const flags = FeatureFlags.getRegisteredFlags(); - describe('#useFeatureFlag', () => { - it('throws an error if the feature flag is not registered', () => { - const Component = () => { - expect(() => { - FeatureFlags.useFeatureFlag('feature-flag-four'); - }).toThrow(/'feature-flag-four' feature flag is not registered/i); + flags.push({ + name: 'registered-flag-4', + pluginId: 'plugin-three', + }); - return null; - }; - - render(withFeatureFlags()); + expect(flags.toObject()).toMatchObject([ + { name: 'registered-flag-1', pluginId: 'plugin-one' }, + { name: 'registered-flag-2', pluginId: 'plugin-one' }, + { name: 'registered-flag-3', pluginId: 'plugin-two' }, + { name: 'registered-flag-4', pluginId: 'plugin-three' }, + ]); }); - it('throws an error if changing value is not recognized', () => { - const Component = () => { - const [, setState] = FeatureFlags.useFeatureFlag('feature-flag-one'); - const renderCount = useRenderCount(); - if (renderCount === 1) { - // @ts-ignore - expect(() => setState('not valid')).toThrow( - /requires a recognized value from the FeatureFlagState/i, - ); - } - return null; - }; + it('should concat the correct values', () => { + const flags = FeatureFlags.getRegisteredFlags(); + const concatValues = flags.concat([ + { + name: 'registered-flag-4', + pluginId: 'plugin-three', + }, + { + name: 'registered-flag-5', + pluginId: 'plugin-four', + }, + ]); - render(withFeatureFlags()); + expect(concatValues).toMatchObject([ + { name: 'registered-flag-1', pluginId: 'plugin-one' }, + { name: 'registered-flag-2', pluginId: 'plugin-one' }, + { name: 'registered-flag-3', pluginId: 'plugin-two' }, + { name: 'registered-flag-4', pluginId: 'plugin-three' }, + { name: 'registered-flag-5', pluginId: 'plugin-four' }, + ]); }); - it('defaults to .NotEnabled', () => { - const Component = () => { - const renderCount = useRenderCount(); - const [state] = FeatureFlags.useFeatureFlag('feature-flag-one'); - if (renderCount === 1) { - expect(state).toEqual(FeatureFlagState.NotEnabled); - } - return null; - }; - - render(withFeatureFlags()); + it('throws an error if length is less than three characters', () => { + const flags = FeatureFlags.getRegisteredFlags(); + expect(() => + flags.push({ + name: 'ab', + pluginId: 'plugin-three', + }), + ).toThrow(/minimum length of three characters/i); }); - it('returns an .Enabled state', () => { - window.localStorage.setItem('featureFlags', '{"feature-flag-one":true}'); - - const Component = () => { - const [state] = FeatureFlags.useFeatureFlag('feature-flag-one'); - const renderCount = useRenderCount(); - if (renderCount === 1) { - expect(state).toEqual(FeatureFlagState.Enabled); - } - return null; - }; - - render(withFeatureFlags()); + it('throws an error if length is greater than 150 characters', () => { + const flags = FeatureFlags.getRegisteredFlags(); + expect(() => + flags.push({ + name: + 'loremipsumdolorsitametconsecteturadipiscingelitnuncvitaeportaexaullamcorperturpismaurisutmattisnequemorbisediaculisauguevivamuspulvinarcursuseratblandithendreritquisqueuttinciduntmagnavestibulumblanditaugueat', + pluginId: 'plugin-three', + }), + ).toThrow(/not exceed 150 characters/i); }); - it('returns an .NotEnabled state', () => { - const Component = () => { - const [state] = FeatureFlags.useFeatureFlag('feature-flag-one'); - const renderCount = useRenderCount(); - if (renderCount === 1) { - expect(state).toEqual(FeatureFlagState.NotEnabled); - } - return null; - }; - - render(withFeatureFlags()); + it('throws an error if name does not start with a lowercase letter', () => { + const flags = FeatureFlags.getRegisteredFlags(); + expect(() => + flags.push({ + name: '123456789', + pluginId: 'plugin-three', + }), + ).toThrow(/start with a lowercase letter/i); }); - it('changes state to .Enabled', () => { - const Component = () => { - const [state, setState] = FeatureFlags.useFeatureFlag( - 'feature-flag-one', - ); - const renderCount = useRenderCount(); - if (renderCount === 1) setState(FeatureFlagState.Enabled); - if (renderCount === 2) expect(state).toEqual(FeatureFlagState.Enabled); - return null; - }; - - render(withFeatureFlags()); - }); - - it('changes state to .NotEnabled', () => { - window.localStorage.setItem('featureFlags', '{"feature-flag-one":true}'); - - const Component = () => { - const [state, setState] = FeatureFlags.useFeatureFlag( - 'feature-flag-one', - ); - const renderCount = useRenderCount(); - if (renderCount === 1) setState(FeatureFlagState.NotEnabled); - if (renderCount === 2) - expect(state).toEqual(FeatureFlagState.NotEnabled); - return null; - }; - - render(withFeatureFlags()); - }); - - it('changes state to .Enabled then .NotEnabled', () => { - const Component = () => { - const [state, setState] = FeatureFlags.useFeatureFlag( - 'feature-flag-one', - ); - const renderCount = useRenderCount(); - if (renderCount === 1) setState(FeatureFlagState.Enabled); - if (renderCount === 2) setState(FeatureFlagState.NotEnabled); - if (renderCount === 3) - expect(state).toEqual(FeatureFlagState.NotEnabled); - return null; - }; - - render(withFeatureFlags()); - }); - - it('changes multiple feature flag states', () => { - expect.assertions(3); - - const Component = () => { - const renderCount = useRenderCount(); - const [stateA, setStateA] = FeatureFlags.useFeatureFlag( - 'feature-flag-one', - ); - const [stateB, setStateB] = FeatureFlags.useFeatureFlag( - 'feature-flag-two', - ); - - useEffect(() => { - if (renderCount === 1) { - setStateA(FeatureFlagState.Enabled); - setStateB(FeatureFlagState.Enabled); - } - if (renderCount === 2) { - expect(stateA).toEqual(FeatureFlagState.Enabled); - expect(stateB).toEqual(FeatureFlagState.Enabled); - expect(window.localStorage.getItem('featureFlags')).toEqual( - '{"feature-flag-one":true,"feature-flag-two":true}', - ); - } - }, [renderCount]); - - return null; - }; - - render(withFeatureFlags()); + it('throws an error if name contains characters other than lowercase letters, numbers and hyphens', () => { + const flags = FeatureFlags.getRegisteredFlags(); + expect(() => + flags.push({ + name: 'Invalid_Feature_Flag', + pluginId: 'plugin-three', + }), + ).toThrow(/only contain lowercase letters, numbers and hyphens/i); }); }); }); diff --git a/packages/core/src/api/app/FeatureFlags.tsx b/packages/core/src/api/app/FeatureFlags.tsx index 172daeb878..3c2983e63f 100644 --- a/packages/core/src/api/app/FeatureFlags.tsx +++ b/packages/core/src/api/app/FeatureFlags.tsx @@ -20,105 +20,158 @@ import { FeatureFlagsApi, } from '../apis/definitions/featureFlags'; -export interface FeatureFlagsEntry { +/** + * Helper method for validating compatibility and flag name. + */ +export function validateBrowserCompat(): void { + if (!('localStorage' in window)) { + throw new Error( + 'Feature Flags are not supported on browsers without the Local Storage API', + ); + } +} + +export function validateFlagName(name: FeatureFlagName): void { + if (name.length < 3) { + throw new Error(`The '${name}' feature flag must have a minimum length of three characters.`); + } + + if (name.length > 150) { + throw new Error(`The '${name}' feature flag must not exceed 150 characters.`); + } + + if (!name.match(/^[a-z]+[a-z0-9-]+$/)) { + throw new Error( + `The '${name}' feature flag must start with a lowercase letter and only contain lowercase letters, numbers and hyphens. ` + + 'Examples: feature-flag-one, alpha, release-2020', + ); + } +} + +/** + * The UserFlags class. + * + * This acts as a data structure for the user's feature flags. You + * can use this to retrieve, add, edit, delete, clear and save the user's + * feature flags to the local browser for persisted storage. + */ +export class UserFlags extends Map { + constructor() { + validateBrowserCompat(); + + try { + const jsonString = window.localStorage.getItem('featureFlags') as string; + const json = JSON.parse(jsonString); + super(Object.entries(json)); + } catch (err) { + super([]); + } + } + + get(name: FeatureFlagName): FeatureFlagState { + validateFlagName(name); + return super.get(name) || FeatureFlagState.NotEnabled; + } + + set(name: FeatureFlagName, state: FeatureFlagState): this { + validateFlagName(name); + super.set(name, state); + this.save(); + return this; + } + + delete(name: FeatureFlagName): boolean { + const exists = !!this.get(name); + super.delete(name); + this.save(); + return exists; + } + + clear(): void { + super.clear(); + this.save(); + } + + save(): void { + window.localStorage.setItem( + 'featureFlags', + JSON.stringify(this.toObject()), + ); + } + + toObject() { + return Array.from(this.entries()).reduce( + (obj, [key, value]) => ({ ...obj, [key]: value }), + {}, + ); + } + + toJSON() { + return JSON.stringify(this.toObject()); + } + + toString() { + return this.toJSON(); + } +} + +/** + * The FeatureFlagsRegistry class. + * + * This acts as a holding data structure for feature flags + * that plugins wish to register for use in Backstage. + */ +export interface FeatureFlagsRegistryItem { pluginId: string; name: FeatureFlagName; } +export class FeatureFlagsRegistry extends Array { + static from(entries: FeatureFlagsRegistryItem[]) { + Array.from(entries).forEach(entry => validateFlagName(entry.name)); + return new FeatureFlagsRegistry(...entries); + } + + push(...entries: FeatureFlagsRegistryItem[]): number { + Array.from(entries).forEach(entry => validateFlagName(entry.name)); + return super.push(...entries); + } + + concat( + ...entries: ( + | FeatureFlagsRegistryItem + | ConcatArray + )[] + ): FeatureFlagsRegistryItem[] { + const _concat = super.concat(...entries); + Array.from(_concat).forEach(entry => validateFlagName(entry.name)); + return _concat; + } + + toObject() { + return [...this.values()]; + } + + toJSON() { + return JSON.stringify(this.toObject()); + } + + toString() { + return this.toJSON(); + } +} + /** * Create the FeatureFlags implementation based on the API. */ +export class FeatureFlags implements FeatureFlagsApi { + public registeredFeatureFlags: FeatureFlagsRegistryItem[] = []; -// TODO: figure out where to put implementations of APIs, both inside apps -// but also in core/separate package. -class FeatureFlagsImpl implements FeatureFlagsApi { - private readonly localStorageKey = 'featureFlags'; - - public constructor(public registeredFeatureFlags: FeatureFlagsEntry[] = []) {} - - private getUserEnabledFeatureFlags(): Set { - if (!('localStorage' in window)) { - throw new Error( - 'Feature Flags are not supported on browsers without the Local Storage API', - ); - } - - try { - const featureFlagsJson = window.localStorage.getItem( - this.localStorageKey, - ); - return new Set( - Object.keys(JSON.parse(featureFlagsJson!)), - ); - } catch (err) { - return new Set(); - } + getFlags(): UserFlags { + return new UserFlags(); } - // We don't make this private as we need this to validate - // in the `registerFeatureFlag` method in the Plugin API. - checkFeatureFlagNameErrors(name: FeatureFlagName): string[] { - const errors: string[] = []; - - if (name.length < 3) { - errors.push('The name must have a minimum length of three characters.'); - } - - if (name.length > 150) { - errors.push('The name must not exceed 150 characters.'); - } - - if (!name.match(/^[a-z]+[a-z0-9-]+$/)) { - errors.push( - 'The name must start with a lowercase letter and only contain lowercase letters, numbers and hyphens. ' + - 'Examples: feature-flag-one, alpha, release-2020', - ); - } - - return errors; - } - - useFeatureFlag( - name: FeatureFlagName, - ): [FeatureFlagState, (state: FeatureFlagState) => void] { - // Check for context - const context = useContext(FeatureFlagsContext); - if (!context) { - throw new Error( - 'No FeatureFlagsContext found. ' + - 'Please use this React Hook in the context of your ', - ); - } - - // Check for errors - // eslint-disable-next-line no-use-before-define - const errors = FeatureFlags.checkFeatureFlagNameErrors(name); - if (errors.length > 0) { - throw new Error(errors[0]); - } - - // Check if the feature flag is registered - const allFlagNames = [...context.featureFlags].map(flag => flag.name); - if (!allFlagNames.includes(name)) { - throw new Error( - `The '${name}' feature flag is not registered by any plugin. ` + - `See the 'registerFeatureFlag' method in the Plugin API (or in your plugin.ts file) on how to register Feature Flags.`, - ); - } - - // eslint-disable-next-line no-use-before-define - const currentState = FeatureFlags.get(context.enabledFeatureFlags, name); - const setState = (state: FeatureFlagState): void => { - // Set the value - // eslint-disable-next-line no-use-before-define - FeatureFlags.set(name, state); - - // Now update the global state - context.refreshEnabledFeatureFlags(); - }; - - return [currentState, setState]; + getRegisteredFlags(): FeatureFlagsRegistry { + return FeatureFlagsRegistry.from(this.registeredFeatureFlags); } } - -export const FeatureFlags = new FeatureFlagsImpl(); diff --git a/packages/core/src/api/plugin/Plugin.tsx b/packages/core/src/api/plugin/Plugin.tsx index d342cb2429..aade5d0092 100644 --- a/packages/core/src/api/plugin/Plugin.tsx +++ b/packages/core/src/api/plugin/Plugin.tsx @@ -21,7 +21,7 @@ import { RouteOptions, FeatureFlagName, } from './types'; -import { FeatureFlags } from '../app/FeatureFlags'; +import { validateBrowserCompat, validateFlagName } from '../app/FeatureFlags'; import { Widget } from '../widgetView/types'; export type PluginConfig = { @@ -54,7 +54,7 @@ export type WidgetHooks = { }; export type FeatureFlagsHooks = { - registerFeatureFlag(name: FeatureFlagName): void; + register(name: FeatureFlagName): void; }; export const registerSymbol = Symbol('plugin-register'); @@ -77,7 +77,6 @@ export default class Plugin { return []; } - const pluginId = this.getId(); const outputs = new Array(); this.config.register({ @@ -95,15 +94,9 @@ export default class Plugin { }, }, featureFlags: { - registerFeatureFlag(name) { - const errors = FeatureFlags.checkFeatureFlagNameErrors(name); - - if (errors.length > 0) { - throw new Error( - `Failed to register '${name}' feature flag in '${pluginId}' plugin: ${errors[0]}`, - ); - } - + register(name) { + validateBrowserCompat(); + validateFlagName(name); outputs.push({ type: 'feature-flag', name }); }, }, diff --git a/plugins/welcome/src/components/WelcomePage/ToggleFeatureFlagButton.test.tsx b/plugins/welcome/src/components/WelcomePage/ToggleFeatureFlagButton.test.tsx index f9be280f50..e5c0b05ecf 100644 --- a/plugins/welcome/src/components/WelcomePage/ToggleFeatureFlagButton.test.tsx +++ b/plugins/welcome/src/components/WelcomePage/ToggleFeatureFlagButton.test.tsx @@ -22,37 +22,27 @@ import { featureFlagsApiRef, ApiProvider, FeatureFlags, - FeatureFlagsContextProvider, } from '@backstage/core'; -function withFeatureFlags(children: ReactNode) { - const featureFlags = new Set([ - { pluginId: 'welcome', name: 'enable-welcome-box' }, - ]); - +function withApiRegistry(component: ReactNode, featureFlags: FeatureFlags) { return ( - - {children} - - ); -} - -function withApiRegistry(children: ReactNode) { - return ( - - {children} + + {component} ); } describe('ToggleFeatureFlagButton', () => { + let featureFlags: FeatureFlags; + beforeEach(() => { + featureFlags = new FeatureFlags(); window.localStorage.clear(); }); it('should enable the feature flag', () => { const rendered = render( - withFeatureFlags(withApiRegistry()), + withApiRegistry(, featureFlags), ); const button = rendered.getByTestId('button-switch-feature-flag-state'); @@ -60,22 +50,22 @@ describe('ToggleFeatureFlagButton', () => { expect(window.localStorage.featureFlags).toBeUndefined(); fireEvent.click(button); - expect(window.localStorage.featureFlags).toBe( - '{"enable-welcome-box":true}', - ); + expect(window.localStorage.featureFlags).toBe('{"enable-welcome-box":1}'); }); it('should disable the feature flag', () => { - const rendered = render( - withFeatureFlags(withApiRegistry()), - ); + const Component = () => + withApiRegistry(, featureFlags); + const rendered = render(); const button = rendered.getByTestId('button-switch-feature-flag-state'); expect(button).toBeInTheDocument(); expect(window.localStorage.featureFlags).toBeUndefined(); fireEvent.click(button); + expect(window.localStorage.featureFlags).toBe('{"enable-welcome-box":1}'); + rendered.rerender(); fireEvent.click(button); - expect(window.localStorage.featureFlags).toBe('{}'); + expect(window.localStorage.featureFlags).toBe('{"enable-welcome-box":0}'); }); }); diff --git a/plugins/welcome/src/components/WelcomePage/ToggleFeatureFlagButton.tsx b/plugins/welcome/src/components/WelcomePage/ToggleFeatureFlagButton.tsx index a940b76263..995339850f 100644 --- a/plugins/welcome/src/components/WelcomePage/ToggleFeatureFlagButton.tsx +++ b/plugins/welcome/src/components/WelcomePage/ToggleFeatureFlagButton.tsx @@ -19,15 +19,16 @@ import { Button } from '@material-ui/core'; import { FeatureFlagState, featureFlagsApiRef, useApi } from '@backstage/core'; const ToggleFeatureFlagButton: FC<{}> = () => { - const { useFeatureFlag } = useApi(featureFlagsApiRef); - const [flagState, setFlagState] = useFeatureFlag('enable-welcome-box'); + const featureFlagsApi = useApi(featureFlagsApiRef); + const flags = featureFlagsApi.getFlags(); + const flagState = flags.get('enable-welcome-box'); const handleClick = () => { - if (flagState === FeatureFlagState.Enabled) { - setFlagState(FeatureFlagState.NotEnabled); - } else { - setFlagState(FeatureFlagState.Enabled); - } + const newValue = flagState + ? FeatureFlagState.NotEnabled + : FeatureFlagState.Enabled; + flags.set('enable-welcome-box', newValue); + window.location.reload(); }; return ( @@ -37,10 +38,9 @@ const ToggleFeatureFlagButton: FC<{}> = () => { onClick={handleClick} data-testid="button-switch-feature-flag-state" > - {flagState === FeatureFlagState.NotEnabled && - 'Enable "enable-welcome-box" feature flag'} - {flagState === FeatureFlagState.Enabled && - 'Disable "enable-welcome-box" feature flag'} + {flagState === FeatureFlagState.NotEnabled + ? 'Disable "enable-welcome-box" feature flag' + : 'Enable "enable-welcome-box" feature flag'} ); }; diff --git a/plugins/welcome/src/plugin.ts b/plugins/welcome/src/plugin.ts index a54a19d4af..e7755862ff 100644 --- a/plugins/welcome/src/plugin.ts +++ b/plugins/welcome/src/plugin.ts @@ -22,6 +22,6 @@ export default createPlugin({ register({ router, featureFlags }) { router.registerRoute('/', WelcomePage); - featureFlags.registerFeatureFlag('enable-welcome-box'); + featureFlags.register('enable-welcome-box'); }, }); From 70fa36ebb42b1d36d7142502d0eade6123a38540 Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Mon, 30 Mar 2020 10:53:46 +0200 Subject: [PATCH 097/146] [core/api/AppBuilder] remove typescript exclamation mark --- packages/core/src/api/app/AppBuilder.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/api/app/AppBuilder.tsx b/packages/core/src/api/app/AppBuilder.tsx index 9aca19b1fe..23d0e17672 100644 --- a/packages/core/src/api/app/AppBuilder.tsx +++ b/packages/core/src/api/app/AppBuilder.tsx @@ -105,7 +105,7 @@ export default class AppBuilder { const FeatureFlags = this.apis && this.apis.get(featureFlagsApiRef); if (FeatureFlags) { - FeatureFlags!.registeredFeatureFlags = registeredFeatureFlags; + FeatureFlags.registeredFeatureFlags = registeredFeatureFlags; } routes.push( From aecaa4e497470722ce02ca348c548c06cf0bf21e Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Mon, 30 Mar 2020 10:57:45 +0200 Subject: [PATCH 098/146] [core/api/FeatureFlags] no validation on get + re-think forwarding output --- packages/core/src/api/app/FeatureFlags.tsx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/core/src/api/app/FeatureFlags.tsx b/packages/core/src/api/app/FeatureFlags.tsx index 3c2983e63f..d1f4ab1313 100644 --- a/packages/core/src/api/app/FeatureFlags.tsx +++ b/packages/core/src/api/app/FeatureFlags.tsx @@ -33,11 +33,15 @@ export function validateBrowserCompat(): void { export function validateFlagName(name: FeatureFlagName): void { if (name.length < 3) { - throw new Error(`The '${name}' feature flag must have a minimum length of three characters.`); + throw new Error( + `The '${name}' feature flag must have a minimum length of three characters.`, + ); } if (name.length > 150) { - throw new Error(`The '${name}' feature flag must not exceed 150 characters.`); + throw new Error( + `The '${name}' feature flag must not exceed 150 characters.`, + ); } if (!name.match(/^[a-z]+[a-z0-9-]+$/)) { @@ -69,22 +73,20 @@ export class UserFlags extends Map { } get(name: FeatureFlagName): FeatureFlagState { - validateFlagName(name); return super.get(name) || FeatureFlagState.NotEnabled; } set(name: FeatureFlagName, state: FeatureFlagState): this { validateFlagName(name); - super.set(name, state); + const output = super.set(name, state); this.save(); - return this; + return output; } delete(name: FeatureFlagName): boolean { - const exists = !!this.get(name); - super.delete(name); + const output = super.delete(name); this.save(); - return exists; + return output; } clear(): void { From d2d8238330fbdec2d424954eab60ef5d90d0e2bf Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Mon, 30 Mar 2020 11:08:15 +0200 Subject: [PATCH 099/146] [core/api] FeatureFlags.test.tsx - s/FeatureFlags/featureFlags/g --- .../core/src/api/app/FeatureFlags.test.tsx | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/packages/core/src/api/app/FeatureFlags.test.tsx b/packages/core/src/api/app/FeatureFlags.test.tsx index 1ad247634c..727001b5e6 100644 --- a/packages/core/src/api/app/FeatureFlags.test.tsx +++ b/packages/core/src/api/app/FeatureFlags.test.tsx @@ -23,14 +23,14 @@ describe('FeatureFlags', () => { }); describe('#getFlags', () => { - let FeatureFlags; + let featureFlags; beforeEach(() => { - FeatureFlags = new FeatureFlagsImpl(); + featureFlags = new FeatureFlagsImpl(); }); it('returns no flags', () => { - expect(FeatureFlags.getFlags().toObject()).toMatchObject({}); + expect(featureFlags.getFlags().toObject()).toMatchObject({}); }); it('returns the correct flags', () => { @@ -43,7 +43,7 @@ describe('FeatureFlags', () => { }), ); - expect(FeatureFlags.getFlags().toObject()).toMatchObject({ + expect(featureFlags.getFlags().toObject()).toMatchObject({ 'feature-flag-one': FeatureFlagState.Enabled, 'feature-flag-two': FeatureFlagState.Enabled, 'feature-flag-three': FeatureFlagState.NotEnabled, @@ -59,19 +59,19 @@ describe('FeatureFlags', () => { }), ); - expect(FeatureFlags.getFlags().get('feature-flag-one')).toEqual( + expect(featureFlags.getFlags().get('feature-flag-one')).toEqual( FeatureFlagState.Enabled, ); - expect(FeatureFlags.getFlags().get('feature-flag-two')).toEqual( + expect(featureFlags.getFlags().get('feature-flag-two')).toEqual( FeatureFlagState.NotEnabled, ); - expect(FeatureFlags.getFlags().get('feature-flag-three')).toEqual( + expect(featureFlags.getFlags().get('feature-flag-three')).toEqual( FeatureFlagState.NotEnabled, ); }); it('sets the correct values', () => { - const flags = FeatureFlags.getFlags(); + const flags = featureFlags.getFlags(); flags.set('feature-flag-zero', FeatureFlagState.Enabled); expect(flags.get('feature-flag-zero')).toEqual(FeatureFlagState.Enabled); @@ -89,7 +89,7 @@ describe('FeatureFlags', () => { }), ); - const flags = FeatureFlags.getFlags(); + const flags = featureFlags.getFlags(); flags.delete('feature-flag-one'); expect(flags.get('feature-flag-one')).toEqual( @@ -110,7 +110,7 @@ describe('FeatureFlags', () => { }), ); - const flags = FeatureFlags.getFlags(); + const flags = featureFlags.getFlags(); flags.clear(); expect(flags.toObject()).toEqual({}); @@ -119,11 +119,11 @@ describe('FeatureFlags', () => { }); describe('#getRegisteredFlags', () => { - let FeatureFlags; + let featureFlags; beforeEach(() => { - FeatureFlags = new FeatureFlagsImpl(); - FeatureFlags.registeredFeatureFlags = [ + featureFlags = new FeatureFlagsImpl(); + featureFlags.registeredFeatureFlags = [ { name: 'registered-flag-1', pluginId: 'plugin-one' }, { name: 'registered-flag-2', pluginId: 'plugin-one' }, { name: 'registered-flag-3', pluginId: 'plugin-two' }, @@ -131,12 +131,12 @@ describe('FeatureFlags', () => { }); it('should return an empty list', () => { - FeatureFlags.registeredFeatureFlags = []; - expect(FeatureFlags.getRegisteredFlags().toObject()).toEqual([]); + featureFlags.registeredFeatureFlags = []; + expect(featureFlags.getRegisteredFlags().toObject()).toEqual([]); }); it('should return an valid list', () => { - expect(FeatureFlags.getRegisteredFlags().toObject()).toMatchObject([ + expect(featureFlags.getRegisteredFlags().toObject()).toMatchObject([ { name: 'registered-flag-1', pluginId: 'plugin-one' }, { name: 'registered-flag-2', pluginId: 'plugin-one' }, { name: 'registered-flag-3', pluginId: 'plugin-two' }, @@ -145,7 +145,7 @@ describe('FeatureFlags', () => { it('should get the correct values', () => { const getByName = name => - FeatureFlags.getRegisteredFlags().find(flag => flag.name === name); + featureFlags.getRegisteredFlags().find(flag => flag.name === name); expect(getByName('registered-flag-0')).toBeUndefined(); expect(getByName('registered-flag-1')).toEqual({ @@ -163,7 +163,7 @@ describe('FeatureFlags', () => { }); it('should append the correct value', () => { - const flags = FeatureFlags.getRegisteredFlags(); + const flags = featureFlags.getRegisteredFlags(); flags.push({ name: 'registered-flag-4', @@ -179,7 +179,7 @@ describe('FeatureFlags', () => { }); it('should concat the correct values', () => { - const flags = FeatureFlags.getRegisteredFlags(); + const flags = featureFlags.getRegisteredFlags(); const concatValues = flags.concat([ { name: 'registered-flag-4', @@ -201,7 +201,7 @@ describe('FeatureFlags', () => { }); it('throws an error if length is less than three characters', () => { - const flags = FeatureFlags.getRegisteredFlags(); + const flags = featureFlags.getRegisteredFlags(); expect(() => flags.push({ name: 'ab', @@ -211,7 +211,7 @@ describe('FeatureFlags', () => { }); it('throws an error if length is greater than 150 characters', () => { - const flags = FeatureFlags.getRegisteredFlags(); + const flags = featureFlags.getRegisteredFlags(); expect(() => flags.push({ name: @@ -222,7 +222,7 @@ describe('FeatureFlags', () => { }); it('throws an error if name does not start with a lowercase letter', () => { - const flags = FeatureFlags.getRegisteredFlags(); + const flags = featureFlags.getRegisteredFlags(); expect(() => flags.push({ name: '123456789', @@ -232,7 +232,7 @@ describe('FeatureFlags', () => { }); it('throws an error if name contains characters other than lowercase letters, numbers and hyphens', () => { - const flags = FeatureFlags.getRegisteredFlags(); + const flags = featureFlags.getRegisteredFlags(); expect(() => flags.push({ name: 'Invalid_Feature_Flag', From 0ff31e486d570eedce8729fa1743bfa1b84d887f Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Mon, 30 Mar 2020 11:11:57 +0200 Subject: [PATCH 100/146] [core/api] store single instance of UserFlags --- packages/core/src/api/app/FeatureFlags.test.tsx | 4 ++++ packages/core/src/api/app/FeatureFlags.tsx | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/core/src/api/app/FeatureFlags.test.tsx b/packages/core/src/api/app/FeatureFlags.test.tsx index 727001b5e6..b145c3e52c 100644 --- a/packages/core/src/api/app/FeatureFlags.test.tsx +++ b/packages/core/src/api/app/FeatureFlags.test.tsx @@ -43,6 +43,7 @@ describe('FeatureFlags', () => { }), ); + featureFlags = new FeatureFlagsImpl(); expect(featureFlags.getFlags().toObject()).toMatchObject({ 'feature-flag-one': FeatureFlagState.Enabled, 'feature-flag-two': FeatureFlagState.Enabled, @@ -59,6 +60,8 @@ describe('FeatureFlags', () => { }), ); + featureFlags = new FeatureFlagsImpl(); + expect(featureFlags.getFlags().get('feature-flag-one')).toEqual( FeatureFlagState.Enabled, ); @@ -89,6 +92,7 @@ describe('FeatureFlags', () => { }), ); + featureFlags = new FeatureFlagsImpl(); const flags = featureFlags.getFlags(); flags.delete('feature-flag-one'); diff --git a/packages/core/src/api/app/FeatureFlags.tsx b/packages/core/src/api/app/FeatureFlags.tsx index d1f4ab1313..fcde669bdd 100644 --- a/packages/core/src/api/app/FeatureFlags.tsx +++ b/packages/core/src/api/app/FeatureFlags.tsx @@ -168,9 +168,10 @@ export class FeatureFlagsRegistry extends Array { */ export class FeatureFlags implements FeatureFlagsApi { public registeredFeatureFlags: FeatureFlagsRegistryItem[] = []; + private readonly userFlags: UserFlags = new UserFlags(); getFlags(): UserFlags { - return new UserFlags(); + return this.userFlags; } getRegisteredFlags(): FeatureFlagsRegistry { From d911da26d2582cfea56191f1ce508ccdfa1c23d5 Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Mon, 30 Mar 2020 11:18:18 +0200 Subject: [PATCH 101/146] [core/api] changed loading pattern to be a static factory method --- packages/core/src/api/app/FeatureFlags.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/core/src/api/app/FeatureFlags.tsx b/packages/core/src/api/app/FeatureFlags.tsx index fcde669bdd..f242683f63 100644 --- a/packages/core/src/api/app/FeatureFlags.tsx +++ b/packages/core/src/api/app/FeatureFlags.tsx @@ -60,15 +60,15 @@ export function validateFlagName(name: FeatureFlagName): void { * feature flags to the local browser for persisted storage. */ export class UserFlags extends Map { - constructor() { + static load(): UserFlags { validateBrowserCompat(); try { const jsonString = window.localStorage.getItem('featureFlags') as string; const json = JSON.parse(jsonString); - super(Object.entries(json)); + return new this(Object.entries(json)); } catch (err) { - super([]); + return new this([]); } } @@ -168,7 +168,7 @@ export class FeatureFlagsRegistry extends Array { */ export class FeatureFlags implements FeatureFlagsApi { public registeredFeatureFlags: FeatureFlagsRegistryItem[] = []; - private readonly userFlags: UserFlags = new UserFlags(); + private readonly userFlags: UserFlags = UserFlags.load(); getFlags(): UserFlags { return this.userFlags; From 70252160e59ecef235c1f8f370879a79bda541d3 Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Mon, 30 Mar 2020 11:39:15 +0200 Subject: [PATCH 102/146] [core] change FeatureFlagState to {On,Off} --- docs/reference/createPlugin-feature-flags.md | 2 +- .../src/api/apis/definitions/featureFlags.ts | 4 ++-- .../core/src/api/app/FeatureFlags.test.tsx | 20 +++++++++---------- packages/core/src/api/app/FeatureFlags.tsx | 5 +++-- .../ToggleFeatureFlagButton.test.tsx | 11 +++++----- .../WelcomePage/ToggleFeatureFlagButton.tsx | 9 +++++---- 6 files changed, 25 insertions(+), 26 deletions(-) diff --git a/docs/reference/createPlugin-feature-flags.md b/docs/reference/createPlugin-feature-flags.md index 6006dc6cb7..60fddaa8fd 100644 --- a/docs/reference/createPlugin-feature-flags.md +++ b/docs/reference/createPlugin-feature-flags.md @@ -51,7 +51,7 @@ const ExampleButton: FC<{}> = () => { const flags = useApi(featureFlagsApiRef).getFlags(); const handleClick = () => { - flags.set('enable-example-feature', FeatureFlagState.Enabled); + flags.set('enable-example-feature', FeatureFlagState.On); }; return ( diff --git a/packages/core/src/api/apis/definitions/featureFlags.ts b/packages/core/src/api/apis/definitions/featureFlags.ts index 95fd02a1cb..926fe091d5 100644 --- a/packages/core/src/api/apis/definitions/featureFlags.ts +++ b/packages/core/src/api/apis/definitions/featureFlags.ts @@ -34,8 +34,8 @@ import { */ export enum FeatureFlagState { - NotEnabled = 0, - Enabled = 1, + Off = 0, + On = 1, } export interface FeatureFlagsApi { diff --git a/packages/core/src/api/app/FeatureFlags.test.tsx b/packages/core/src/api/app/FeatureFlags.test.tsx index b145c3e52c..b5bbb4d68e 100644 --- a/packages/core/src/api/app/FeatureFlags.test.tsx +++ b/packages/core/src/api/app/FeatureFlags.test.tsx @@ -45,9 +45,9 @@ describe('FeatureFlags', () => { featureFlags = new FeatureFlagsImpl(); expect(featureFlags.getFlags().toObject()).toMatchObject({ - 'feature-flag-one': FeatureFlagState.Enabled, - 'feature-flag-two': FeatureFlagState.Enabled, - 'feature-flag-three': FeatureFlagState.NotEnabled, + 'feature-flag-one': FeatureFlagState.On, + 'feature-flag-two': FeatureFlagState.On, + 'feature-flag-three': FeatureFlagState.Off, }); }); @@ -63,21 +63,21 @@ describe('FeatureFlags', () => { featureFlags = new FeatureFlagsImpl(); expect(featureFlags.getFlags().get('feature-flag-one')).toEqual( - FeatureFlagState.Enabled, + FeatureFlagState.On, ); expect(featureFlags.getFlags().get('feature-flag-two')).toEqual( - FeatureFlagState.NotEnabled, + FeatureFlagState.Off, ); expect(featureFlags.getFlags().get('feature-flag-three')).toEqual( - FeatureFlagState.NotEnabled, + FeatureFlagState.Off, ); }); it('sets the correct values', () => { const flags = featureFlags.getFlags(); - flags.set('feature-flag-zero', FeatureFlagState.Enabled); + flags.set('feature-flag-zero', FeatureFlagState.On); - expect(flags.get('feature-flag-zero')).toEqual(FeatureFlagState.Enabled); + expect(flags.get('feature-flag-zero')).toEqual(FeatureFlagState.On); expect(window.localStorage.getItem('featureFlags')).toEqual( '{"feature-flag-zero":1}', ); @@ -96,9 +96,7 @@ describe('FeatureFlags', () => { const flags = featureFlags.getFlags(); flags.delete('feature-flag-one'); - expect(flags.get('feature-flag-one')).toEqual( - FeatureFlagState.NotEnabled, - ); + expect(flags.get('feature-flag-one')).toEqual(FeatureFlagState.Off); expect(window.localStorage.getItem('featureFlags')).toEqual( '{"feature-flag-two":0}', ); diff --git a/packages/core/src/api/app/FeatureFlags.tsx b/packages/core/src/api/app/FeatureFlags.tsx index f242683f63..8e5a06b209 100644 --- a/packages/core/src/api/app/FeatureFlags.tsx +++ b/packages/core/src/api/app/FeatureFlags.tsx @@ -73,7 +73,7 @@ export class UserFlags extends Map { } get(name: FeatureFlagName): FeatureFlagState { - return super.get(name) || FeatureFlagState.NotEnabled; + return super.get(name) || FeatureFlagState.Off; } set(name: FeatureFlagName, state: FeatureFlagState): this { @@ -168,9 +168,10 @@ export class FeatureFlagsRegistry extends Array { */ export class FeatureFlags implements FeatureFlagsApi { public registeredFeatureFlags: FeatureFlagsRegistryItem[] = []; - private readonly userFlags: UserFlags = UserFlags.load(); + private userFlags: UserFlags | undefined; getFlags(): UserFlags { + if (!this.userFlags) this.userFlags = UserFlags.load(); return this.userFlags; } diff --git a/plugins/welcome/src/components/WelcomePage/ToggleFeatureFlagButton.test.tsx b/plugins/welcome/src/components/WelcomePage/ToggleFeatureFlagButton.test.tsx index e5c0b05ecf..13d6741cc7 100644 --- a/plugins/welcome/src/components/WelcomePage/ToggleFeatureFlagButton.test.tsx +++ b/plugins/welcome/src/components/WelcomePage/ToggleFeatureFlagButton.test.tsx @@ -54,17 +54,16 @@ describe('ToggleFeatureFlagButton', () => { }); it('should disable the feature flag', () => { - const Component = () => - withApiRegistry(, featureFlags); - const rendered = render(); + window.localStorage.setItem('featureFlags', '{"enable-welcome-box":1}'); + + const rendered = render( + withApiRegistry(, featureFlags), + ); const button = rendered.getByTestId('button-switch-feature-flag-state'); expect(button).toBeInTheDocument(); - expect(window.localStorage.featureFlags).toBeUndefined(); - fireEvent.click(button); expect(window.localStorage.featureFlags).toBe('{"enable-welcome-box":1}'); - rendered.rerender(); fireEvent.click(button); expect(window.localStorage.featureFlags).toBe('{"enable-welcome-box":0}'); }); diff --git a/plugins/welcome/src/components/WelcomePage/ToggleFeatureFlagButton.tsx b/plugins/welcome/src/components/WelcomePage/ToggleFeatureFlagButton.tsx index 995339850f..3ce66ce1c7 100644 --- a/plugins/welcome/src/components/WelcomePage/ToggleFeatureFlagButton.tsx +++ b/plugins/welcome/src/components/WelcomePage/ToggleFeatureFlagButton.tsx @@ -24,9 +24,10 @@ const ToggleFeatureFlagButton: FC<{}> = () => { const flagState = flags.get('enable-welcome-box'); const handleClick = () => { - const newValue = flagState - ? FeatureFlagState.NotEnabled - : FeatureFlagState.Enabled; + const newValue = + flagState === FeatureFlagState.On + ? FeatureFlagState.Off + : FeatureFlagState.On; flags.set('enable-welcome-box', newValue); window.location.reload(); }; @@ -38,7 +39,7 @@ const ToggleFeatureFlagButton: FC<{}> = () => { onClick={handleClick} data-testid="button-switch-feature-flag-state" > - {flagState === FeatureFlagState.NotEnabled + {flagState === FeatureFlagState.On ? 'Disable "enable-welcome-box" feature flag' : 'Enable "enable-welcome-box" feature flag'} From dc363e43fc80872cee4d32eb38dc3e8925132efe Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 30 Mar 2020 12:46:49 +0200 Subject: [PATCH 103/146] packages: rename @backstage/app to example-app --- docs/getting-started/development-environment.md | 2 +- package.json | 2 +- packages/app/README.md | 2 +- packages/app/package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/getting-started/development-environment.md b/docs/getting-started/development-environment.md index e836a0c5d2..bb5b24333e 100644 --- a/docs/getting-started/development-environment.md +++ b/docs/getting-started/development-environment.md @@ -15,7 +15,7 @@ By default, backstage will start on port 3000, however you can override this by Once successfully started, you should see the following message in your terminal window: ``` -You can now view @backstage/app in the browser. +You can now view example-app in the browser. Local: http://localhost:8080 On Your Network: http://192.168.1.224:8080 diff --git a/package.json b/package.json index 736da36ac9..eaf85f7567 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "node": ">=12.0.0" }, "scripts": { - "start": "yarn build && yarn workspace @backstage/app start", + "start": "yarn build && yarn workspace example-app start", "build": "lerna run build", "test": "cross-env CI=true lerna run test --since origin/master -- --coverage", "create-plugin": "backstage-cli create-plugin", diff --git a/packages/app/README.md b/packages/app/README.md index 0e4aa9912e..a2c7ae1b77 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -1,3 +1,3 @@ -# @backstage/app +# example-app This package is an example of a Backstage application. diff --git a/packages/app/package.json b/packages/app/package.json index 8e66ab7b5e..c332d2f0da 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,5 +1,5 @@ { - "name": "@backstage/app", + "name": "example-app", "version": "0.1.1-alpha.0", "private": true, "dependencies": { From 301bd669197a81ff96d357744ae6d90162da9cb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 30 Mar 2020 14:24:22 +0200 Subject: [PATCH 104/146] [cli] CODEOWNERS: catch-all goes first, and paths are absolute --- packages/cli/src/commands/create-plugin/createPlugin.ts | 2 +- packages/cli/src/commands/create-plugin/lib/codeowners.ts | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/commands/create-plugin/createPlugin.ts b/packages/cli/src/commands/create-plugin/createPlugin.ts index 4d264b01f9..da695bee95 100644 --- a/packages/cli/src/commands/create-plugin/createPlugin.ts +++ b/packages/cli/src/commands/create-plugin/createPlugin.ts @@ -405,7 +405,7 @@ const createPlugin = async () => { if (ownerIds && ownerIds.length) { await addCodeownersEntry( codeownersPath!, - path.join('plugins', answers.id), + `/plugins/${answers.id}`, ownerIds, ); } diff --git a/packages/cli/src/commands/create-plugin/lib/codeowners.ts b/packages/cli/src/commands/create-plugin/lib/codeowners.ts index 1ffa640f04..91b0e19782 100644 --- a/packages/cli/src/commands/create-plugin/lib/codeowners.ts +++ b/packages/cli/src/commands/create-plugin/lib/codeowners.ts @@ -20,6 +20,7 @@ import path from 'path'; const TEAM_ID_RE = /^@[-\w]+\/[-\w]+$/; const USER_ID_RE = /^@[-\w]+$/; const EMAIL_RE = /^[^@]+@[-.\w]+\.[-\w]+$/i; +const DEFAULT_OWNER = '@spotify/backstage-core'; type CodeownersEntry = { ownedPath: string; @@ -96,8 +97,11 @@ export async function addCodeownersEntry( const newDeclarationEntries = oldDeclarationEntries .filter(entry => entry.ownedPath !== '*') .concat([{ ownedPath, ownerIds }]) - .sort((l1, l2) => l1.ownedPath.localeCompare(l2.ownedPath)) - .concat([{ ownedPath: '*', ownerIds: ['@spotify/backstage-core'] }]); + .sort((l1, l2) => l1.ownedPath.localeCompare(l2.ownedPath)); + newDeclarationEntries.unshift({ + ownedPath: '*', + ownerIds: [DEFAULT_OWNER], + }); // Calculate longest path to be able to align entries nicely const longestOwnedPath = newDeclarationEntries.reduce( From 575a775e453006608e872bbb6d960e75d59a4013 Mon Sep 17 00:00:00 2001 From: Mateus Marquezini Date: Mon, 30 Mar 2020 11:55:44 -0300 Subject: [PATCH 105/146] #400 code review changes --- .../core/src/layout/InfoCard/BottomLink.js | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/packages/core/src/layout/InfoCard/BottomLink.js b/packages/core/src/layout/InfoCard/BottomLink.js index c3d7f1efc0..5dafa72978 100644 --- a/packages/core/src/layout/InfoCard/BottomLink.js +++ b/packages/core/src/layout/InfoCard/BottomLink.js @@ -21,22 +21,19 @@ import { Divider, ListItemText, withStyles } from '@material-ui/core'; import { ListItem, ListItemIcon } from '@material-ui/core'; import ArrowIcon from '@material-ui/icons/ArrowForward'; import grey from '@material-ui/core/colors/grey'; +import Box from '@material-ui/core/Box'; -const LinkBottomText = withStyles({ - primary: { - fontWeight: '700', - color: grey[900], - }, -})(ListItemText); - -const ListItemBottom = withStyles({ +const styles = theme => ({ root: { maxWidth: 'fit-content', - padding: '16px 16px 16px 20px', + padding: theme.spacing(2, 2, 2, 2.5) }, -})(ListItem); - -export default class BottomLink extends Component { + boxTitle: { + margin: 0, + color: grey[900] + } +}) +class BottomLink extends Component { static propTypes = { link: PropTypes.string, title: PropTypes.string, @@ -44,19 +41,23 @@ export default class BottomLink extends Component { }; render() { - const { link, title, onClick } = this.props; + const { link, title, onClick, classes } = this.props; return (
- - {title} + + + {title} + - +
); } } + +export default withStyles(styles)(BottomLink); \ No newline at end of file From ea4b85849ccf9fa5a398e419152873382e0f8d16 Mon Sep 17 00:00:00 2001 From: Mateus Marquezini Date: Mon, 30 Mar 2020 15:09:38 -0300 Subject: [PATCH 106/146] #400 code review changes --- packages/core/src/layout/InfoCard/InfoCard.js | 13 +++++++++---- .../home-page/src/components/HomePage/HomePage.tsx | 2 +- .../src/components/WelcomePage/WelcomePage.tsx | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/core/src/layout/InfoCard/InfoCard.js b/packages/core/src/layout/InfoCard/InfoCard.js index 0b2abfb164..1005a016af 100644 --- a/packages/core/src/layout/InfoCard/InfoCard.js +++ b/packages/core/src/layout/InfoCard/InfoCard.js @@ -29,13 +29,17 @@ import BottomLink from './BottomLink'; import textContent from 'react-addons-text-content'; +const styles = theme => ({ + header: { + padding: theme.spacing(2, 2, 2, 2.5) + } +}); + const BoldHeader = withStyles({ title: { fontWeight: '700' }, subheader: { paddingTop: '2px' }, - root: { - padding: '16px 16px 16px 20px', - }, })(CardHeader); + const CardActionsTopRight = withStyles({ root: { display: 'inline-block', @@ -191,6 +195,7 @@ class InfoCard extends Component { {title && ( <> = () => { Things you own - + diff --git a/plugins/welcome/src/components/WelcomePage/WelcomePage.tsx b/plugins/welcome/src/components/WelcomePage/WelcomePage.tsx index 69a892a95e..f0dfc53418 100644 --- a/plugins/welcome/src/components/WelcomePage/WelcomePage.tsx +++ b/plugins/welcome/src/components/WelcomePage/WelcomePage.tsx @@ -53,7 +53,7 @@ const WelcomePage: FC<{}> = () => { - + You now have a running instance of Backstage! From 8782465459fcfc60d4ee81006c931d38af0e8820 Mon Sep 17 00:00:00 2001 From: Szymon Graczyk Date: Mon, 30 Mar 2020 22:25:49 +0200 Subject: [PATCH 107/146] Rewrite layout/Header/Waves to use TypeScript * Add simple test looking for svg node --- .../core/src/layout/Header/Waves.test.tsx | 27 +++++++++++++++++++ .../src/layout/Header/{Waves.js => Waves.tsx} | 14 +++++++--- 2 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 packages/core/src/layout/Header/Waves.test.tsx rename packages/core/src/layout/Header/{Waves.js => Waves.tsx} (94%) diff --git a/packages/core/src/layout/Header/Waves.test.tsx b/packages/core/src/layout/Header/Waves.test.tsx new file mode 100644 index 0000000000..940b845d00 --- /dev/null +++ b/packages/core/src/layout/Header/Waves.test.tsx @@ -0,0 +1,27 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import Waves from './Waves'; +import { pageTheme } from '../Page/PageThemeProvider'; + +describe('', () => { + it('should render svg', () => { + const rendered = render(); + rendered.getByTestId('wave-svg'); + }); +}); diff --git a/packages/core/src/layout/Header/Waves.js b/packages/core/src/layout/Header/Waves.tsx similarity index 94% rename from packages/core/src/layout/Header/Waves.js rename to packages/core/src/layout/Header/Waves.tsx index 52523834dc..c23afe69e3 100644 --- a/packages/core/src/layout/Header/Waves.js +++ b/packages/core/src/layout/Header/Waves.tsx @@ -14,10 +14,11 @@ * limitations under the License. */ -import React from 'react'; +import React, { FC } from 'react'; import { makeStyles } from '@material-ui/core'; +import { PageTheme } from '@backstage/core'; -const useStyles = makeStyles({ +const useStyles = makeStyles({ wave: { position: 'absolute', height: '100%', @@ -26,11 +27,15 @@ const useStyles = makeStyles({ bottom: 0, left: 0, right: 0, - 'z-index': -1, + zIndex: -1, }, }); -const Waves = ({ theme }) => { +type Props = { + theme: PageTheme; +}; + +const Waves: FC = ({ theme }) => { const classes = useStyles(); const [color1, color2] = theme.gradient.colors; @@ -41,6 +46,7 @@ const Waves = ({ theme }) => { fill="none" xmlns="http://www.w3.org/2000/svg" className={classes.wave} + data-testid="wave-svg" > From 39f7fe277f8888aa8305807de2ad57cc9789c0fb Mon Sep 17 00:00:00 2001 From: Szymon Graczyk Date: Mon, 30 Mar 2020 22:31:43 +0200 Subject: [PATCH 108/146] Correct the import of PageTheme to use relative path --- packages/core/src/layout/Header/Waves.test.tsx | 2 +- packages/core/src/layout/Header/Waves.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/layout/Header/Waves.test.tsx b/packages/core/src/layout/Header/Waves.test.tsx index 940b845d00..2c7d463052 100644 --- a/packages/core/src/layout/Header/Waves.test.tsx +++ b/packages/core/src/layout/Header/Waves.test.tsx @@ -16,8 +16,8 @@ import React from 'react'; import { render } from '@testing-library/react'; -import Waves from './Waves'; import { pageTheme } from '../Page/PageThemeProvider'; +import Waves from './Waves'; describe('', () => { it('should render svg', () => { diff --git a/packages/core/src/layout/Header/Waves.tsx b/packages/core/src/layout/Header/Waves.tsx index c23afe69e3..f37ecf7b12 100644 --- a/packages/core/src/layout/Header/Waves.tsx +++ b/packages/core/src/layout/Header/Waves.tsx @@ -16,7 +16,7 @@ import React, { FC } from 'react'; import { makeStyles } from '@material-ui/core'; -import { PageTheme } from '@backstage/core'; +import { PageTheme } from '../Page'; const useStyles = makeStyles({ wave: { From f4d8425d19d453e60322608c98a1f09d156436a5 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 31 Mar 2020 00:09:39 +0200 Subject: [PATCH 109/146] cli/templates: fix InfoCard usage in plugin template --- .../src/components/ExampleComponent/ExampleComponent.tsx.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.tsx.hbs b/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.tsx.hbs index b96c970022..f0f148db48 100644 --- a/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.tsx.hbs +++ b/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.tsx.hbs @@ -40,7 +40,7 @@ const ExampleComponent: FC<{}> = () => ( - + All content should be wrapped in a card like this. From b6bc98736246cd4cde4ae216882c06dffca53b8f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 31 Mar 2020 00:09:54 +0200 Subject: [PATCH 110/146] github/workflows: run cli e2e tests on changes to core --- .github/workflows/cli.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index e7d8c53b90..754cd66a63 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -2,7 +2,10 @@ name: CLI Test on: pull_request: - paths: ['.github/workflows/cli.yml', 'packages/cli/**'] + paths: + - '.github/workflows/cli.yml' + - 'packages/cli/**' + - 'packages/core/**' types: [opened, reopened, edited, synchronize] jobs: From 9a5ed723c48c3d2f932b2c28741db4435bdf8fe0 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 31 Mar 2020 00:17:15 +0200 Subject: [PATCH 111/146] deployment: merge into root dockerfile --- Dockerfile | 9 ++++++- deployment/Dockerfile.heroku | 24 ------------------- deployment/default.conf.template | 8 ------- .../{default.conf => default.conf.template} | 3 +-- 4 files changed, 9 insertions(+), 35 deletions(-) delete mode 100644 deployment/Dockerfile.heroku delete mode 100644 deployment/default.conf.template rename docker/{default.conf => default.conf.template} (95%) diff --git a/Dockerfile b/Dockerfile index 5337207235..f26f69297b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,4 +15,11 @@ RUN yarn build FROM nginx:mainline COPY --from=builder /app/packages/app/build /usr/share/nginx/html -COPY ./docker/default.conf /etc/nginx/conf.d/default.conf + +# Run nginx as root +RUN sed -i 's/user nginx.*$//' /etc/nginx/nginx.conf + +COPY docker/default.conf.template /etc/nginx/conf.d/default.conf.template +CMD /bin/bash -c "envsubst '\$PORT' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf" && nginx -g 'daemon off;' + +ENV PORT 80 diff --git a/deployment/Dockerfile.heroku b/deployment/Dockerfile.heroku deleted file mode 100644 index 15878c67da..0000000000 --- a/deployment/Dockerfile.heroku +++ /dev/null @@ -1,24 +0,0 @@ -FROM node:12 as builder -WORKDIR /app - -COPY package.json yarn.lock .yarnrc .npmrc /app/ -COPY .yarn /app/.yarn -COPY packages /app/packages -COPY plugins /app/plugins - -RUN yarn - -COPY . . - -RUN yarn build - -FROM nginx:mainline - -COPY --from=builder /app/packages/app/build /usr/share/nginx/html - -# Run nginx as root -RUN sed -i 's/user nginx.*$//' /etc/nginx/nginx.conf - -# Copy in the nginx conf template and replace "$PORT" with the environment variable set by heroku -COPY deployment/default.conf.template /etc/nginx/conf.d/default.conf.template -CMD /bin/bash -c "envsubst '\$PORT' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf" && nginx -g 'daemon off;' diff --git a/deployment/default.conf.template b/deployment/default.conf.template deleted file mode 100644 index 61c32aa95e..0000000000 --- a/deployment/default.conf.template +++ /dev/null @@ -1,8 +0,0 @@ -server { - listen $PORT default_server; - - location / { - root /usr/share/nginx/html; - index index.html; - } -} diff --git a/docker/default.conf b/docker/default.conf.template similarity index 95% rename from docker/default.conf rename to docker/default.conf.template index d01be24dc7..29252f9625 100644 --- a/docker/default.conf +++ b/docker/default.conf.template @@ -1,5 +1,5 @@ server { - listen 80; + listen $PORT; server_name localhost; #charset koi8-r; @@ -20,4 +20,3 @@ server { root /usr/share/nginx/html; } } - From afb77c88239671f3956acd5aec83bf3011c04a12 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 31 Mar 2020 00:51:12 +0200 Subject: [PATCH 112/146] DEPLOYMENT: update heroku deployment instructions --- DEPLOYMENT.md | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 47cd67e8ef..be77f0d4de 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -17,18 +17,7 @@ You _might_ also need to set your Heroku app's stack to `container` $ heroku stack:set container -a ``` -There are two small tweaks made to Backstage's default Dockerfile to run on Heroku: - -1. Do not run nginx as the `nginx` user. -2. Replace the standard port (80) in `/etc/nginx/conf.d/default.conf` with the wildcard `$PORT`, which Heroku will set for us. - -These changes are in the `deployment/Dockerfile.heroku` file, which you will need to copy into your root directory before deploying: - -```bash -$ cp deployment/Dockerfile.heroku Dockerfile -``` - -With these changes, we can build/push the Docker image to Heroku's container registry and release it to the `web` worker. +We can now build/push the Docker image to Heroku's container registry and release it to the `web` worker. ```bash $ heroku container:push web -a From ae3d5ffe9df5740acfdc0035e7a111e3d2296fe3 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 31 Mar 2020 00:51:34 +0200 Subject: [PATCH 113/146] Dockerfile: optimize copying a bit --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f26f69297b..8415a5eebc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,8 @@ COPY plugins /app/plugins RUN yarn -COPY . . +COPY lerna.json tsconfig.json .eslintignore .eslintrc.js /app/ +COPY scripts/ /app/scripts RUN yarn build From 912bd3ee18f603f02eee363e4a4053f147a52514 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 31 Mar 2020 01:11:34 +0200 Subject: [PATCH 114/146] Dockerfile: move nginx config templating to run script --- Dockerfile | 6 ++---- docker/run.sh | 9 +++++++++ 2 files changed, 11 insertions(+), 4 deletions(-) create mode 100755 docker/run.sh diff --git a/Dockerfile b/Dockerfile index 8415a5eebc..cab9ede03f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,10 +17,8 @@ FROM nginx:mainline COPY --from=builder /app/packages/app/build /usr/share/nginx/html -# Run nginx as root -RUN sed -i 's/user nginx.*$//' /etc/nginx/nginx.conf - COPY docker/default.conf.template /etc/nginx/conf.d/default.conf.template -CMD /bin/bash -c "envsubst '\$PORT' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf" && nginx -g 'daemon off;' +COPY docker/run.sh /usr/local/bin/run.sh +CMD run.sh ENV PORT 80 diff --git a/docker/run.sh b/docker/run.sh new file mode 100755 index 0000000000..7bc5f5fa39 --- /dev/null +++ b/docker/run.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +# Run nginx as root +sed -i 's/user nginx.*$//' /etc/nginx/nginx.conf + +# Write selected env vars to nginx config +envsubst '$PORT' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf + +exec nginx -g 'daemon off;' From cf082b8a540459efb985aeda7367eeb8c2e283df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Tue, 31 Mar 2020 09:14:39 +0200 Subject: [PATCH 115/146] Remove internal specific code --- .../layout/HeaderLabel/OwnerHeaderLabel.js | 80 ------------------- .../HeaderLabel/OwnerHeaderLabel.test.js | 63 --------------- 2 files changed, 143 deletions(-) delete mode 100644 packages/core/src/layout/HeaderLabel/OwnerHeaderLabel.js delete mode 100644 packages/core/src/layout/HeaderLabel/OwnerHeaderLabel.test.js diff --git a/packages/core/src/layout/HeaderLabel/OwnerHeaderLabel.js b/packages/core/src/layout/HeaderLabel/OwnerHeaderLabel.js deleted file mode 100644 index 44a63990dc..0000000000 --- a/packages/core/src/layout/HeaderLabel/OwnerHeaderLabel.js +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { Tooltip, Link, withStyles } from '@material-ui/core'; - -import { StatusError } from '../../components/Status'; -import HeaderLabel from './HeaderLabel'; - -const style = theme => ({ - notVerified: { - color: theme.palette.status.error, - borderRadius: 4, - padding: '3px 6px', - fontSize: '8pt', - opacity: 0.8, - fontWeight: 'bold', - position: 'relative', - top: -4, - backgroundColor: 'pink', - float: 'right', - marginLeft: 14, - }, - label: { float: 'left' }, -}); - -class OwnerHeaderLabel extends Component { - static propTypes = { - owner: PropTypes.object.isRequired, - }; - - render() { - const { owner, classes } = this.props; - const isBadSquad = owner.type !== 'squad'; - - const notVerified = isBadSquad && ( - - - Squad not verified! - - - ); - const label = ( - - - {owner.name} - - - ); - return ( - <> - - {notVerified} - - ); - } -} - -export default withStyles(style)(OwnerHeaderLabel); diff --git a/packages/core/src/layout/HeaderLabel/OwnerHeaderLabel.test.js b/packages/core/src/layout/HeaderLabel/OwnerHeaderLabel.test.js deleted file mode 100644 index 9b220b3cc5..0000000000 --- a/packages/core/src/layout/HeaderLabel/OwnerHeaderLabel.test.js +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React from 'react'; -import { render } from '@testing-library/react'; -import OwnerHeaderLabel from './OwnerHeaderLabel'; -import { wrapInThemedTestApp } from '../../testUtils'; - -const properOwner = { id: 'tools', name: 'tools', type: 'squad' }; -const badOwner = { id: 'tools-xxx', name: 'tools-xxx' }; - -describe('', () => { - it('should have a label', () => { - const rendered = render( - wrapInThemedTestApp(), - ); - expect(rendered.getByText('Owner')).toBeInTheDocument(); - expect(rendered.getByText('tools')).toBeInTheDocument(); - expect(rendered.queryByText('Squad not verified!')).not.toBeInTheDocument(); - }); - - it('should have an org link', () => { - const rendered = render( - wrapInThemedTestApp(), - ); - const anchor = rendered.container.querySelector('a'); - expect(anchor.href).toBe('http://localhost/org/tools'); - }); - - it('should have WARNING label', () => { - const rendered = render( - wrapInThemedTestApp(), - ); - expect(rendered.getByText('Squad not verified!')).toBeInTheDocument(); - }); - - it('should have status error label', () => { - const rendered = render( - wrapInThemedTestApp(), - ); - expect(rendered.getByLabelText('Status error')).toBeInTheDocument(); - }); - - it('should handle empty input', () => { - const rendered = render( - wrapInThemedTestApp(), - ); - expect(rendered.getByLabelText('Status error')).toBeInTheDocument(); - }); -}); From e22d6dd2530e5c460fb75618f124dc39e8bb315a Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 30 Mar 2020 23:50:49 +0200 Subject: [PATCH 116/146] github/workflows: run yarn install with --frozen-lockfile --- .github/workflows/cli.yml | 3 ++- .github/workflows/frontend.yml | 3 ++- .github/workflows/master.yml | 3 ++- .github/workflows/storybook-deploy.yml | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index 754cd66a63..75173cbcc5 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -34,7 +34,8 @@ jobs: uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - - run: yarn install + - name: yarn install + run: yarn install --frozen-lockfile - run: yarn build # This creates a new plugin and pollutes the workspace, so it should be run last. - name: verify app serve and plugin creation on Windows diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index 22fc24df23..8095106910 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -38,7 +38,8 @@ jobs: uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - - run: yarn install + - name: yarn install + run: yarn install --frozen-lockfile - run: yarn lint - run: yarn build - run: yarn test diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 1985215a0e..e82c6c3a4b 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -37,7 +37,8 @@ jobs: with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - - run: yarn install + - name: yarn install + run: yarn install --frozen-lockfile - run: yarn lint - run: yarn build - run: yarn test diff --git a/.github/workflows/storybook-deploy.yml b/.github/workflows/storybook-deploy.yml index 9a266144bc..c7d458b14b 100644 --- a/.github/workflows/storybook-deploy.yml +++ b/.github/workflows/storybook-deploy.yml @@ -39,7 +39,8 @@ jobs: with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - - run: yarn install + - name: yarn install + run: yarn install --frozen-lockfile - name: build storybook run: yarn workspace storybook build-storybook - name: deploy storybook to gh-pages From 555db720ae17e38946787da393c9e0e51a17ff68 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 26 Mar 2020 14:57:54 +0100 Subject: [PATCH 117/146] cli: added initial build-cache command --- .../cli/src/commands/build-cache/index.ts | 22 +++++++++++++++++++ packages/cli/src/index.ts | 18 +++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 packages/cli/src/commands/build-cache/index.ts diff --git a/packages/cli/src/commands/build-cache/index.ts b/packages/cli/src/commands/build-cache/index.ts new file mode 100644 index 0000000000..4cf3d8ef02 --- /dev/null +++ b/packages/cli/src/commands/build-cache/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * The build-cache command is used to make builds a no-op if there are no changes to the package. + * It supports both local development where the output directory remains intact, as well as CI + * where the output directory is stored in a separate cache dir. + */ +export default async () => {}; diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 5f24190237..997c05c18c 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -19,6 +19,7 @@ import chalk from 'chalk'; import fs from 'fs'; import createPluginCommand from './commands/create-plugin/createPlugin'; import watch from './commands/watch-deps'; +import buildCache from './commands/build-cache'; import lintCommand from './commands/lint'; import testCommand from './commands/testCommand'; import appBuild from './commands/app/build'; @@ -76,6 +77,23 @@ const main = (argv: string[]) => { .description('Watch all dependencies while running another command') .action(actionHandler(watch)); + program + .command('build-cache') + .description('Wrap build command with a cache') + .option( + '--input ', + 'List of input directories that invalidate the cache [.]', + (value, acc) => acc.concat(value), + [], + ) + .option('--output ', 'Output directory to cache', 'dist') + .option( + '--cache-dir ', + 'Cache dir', + '/node_modules/.cache/backstage-builds', + ) + .action(actionHandler(buildCache)); + program.on('command:*', () => { console.log(); console.log( From de34ab58e29d560c30183400a93c069cd7980d2f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 27 Mar 2020 10:21:44 +0100 Subject: [PATCH 118/146] cli/commands/build-cache: initial caching without build --- .../cli/src/commands/build-cache/index.ts | 155 +++++++++++++++++- 1 file changed, 154 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/commands/build-cache/index.ts b/packages/cli/src/commands/build-cache/index.ts index 4cf3d8ef02..2d9a33d9f2 100644 --- a/packages/cli/src/commands/build-cache/index.ts +++ b/packages/cli/src/commands/build-cache/index.ts @@ -14,9 +14,162 @@ * limitations under the License. */ +import fs from 'fs-extra'; +import { resolve as resolvePath, relative as relativePath } from 'path'; +import { promisify } from 'util'; +import { exec as execCb } from 'child_process'; +import { Command } from 'commander'; +import { ExitCodeError } from '../../helpers/errors'; +const exec = promisify(execCb); + +const INFO_FILE = '.backstage-build-cache'; + +type Options = { + inputs: string[]; + output: string; + cacheDir: string; + repoRoot: string; +}; + +async function parseOptions(cmd: Command): Promise { + const repoRoot = await run('git rev-parse --show-toplevel'); + const argTransformer = (arg: string) => + resolvePath(arg.replace(//g, repoRoot).replace(/'/g, '')); + + const inputs = cmd.input.map(argTransformer) as string[]; + if (inputs.length === 0) { + inputs.push(argTransformer('.')); + } + const output = argTransformer(cmd.output); + const cacheDir = argTransformer(cmd.cacheDir); + return { inputs, output, cacheDir, repoRoot }; +} + /* * The build-cache command is used to make builds a no-op if there are no changes to the package. * It supports both local development where the output directory remains intact, as well as CI * where the output directory is stored in a separate cache dir. */ -export default async () => {}; +export default async (cmd: Command, args: string[]) => { + const options = await parseOptions(cmd); + console.log('DEBUG: options =', options); + console.log('DEBUG: args =', args); + + const cache = await readCache(options); + console.log('DEBUG: cache =', cache); + + const trees = await getInputHashes(options); + console.log('DEBUG: trees =', trees); + + const cacheHit = cache.readable && cache.trees?.join(',') === trees.join(','); + if (!cacheHit) { + await build(options); + + await fs.writeFile( + resolvePath(options.output, INFO_FILE), + JSON.stringify({ trees }, null, 2), + 'utf-8', + ); + if (cache.writable) { + console.log(`DEBUG: write cache`); + console.log('DEBUG: cache.location =', cache.location); + await fs.remove(cache.location); + console.log('DEBUG: options.output =', options.output); + await fs.copy(options.output, cache.location); + } + } else if (cache.needsCopy) { + console.log(`DEBUG: would copy cache`); + await fs.remove(options.output); + await fs.copy(cache.location, options.output); + } + + const ls = await run('ls derp'); + console.log('DEBUG: ls =', ls); +}; + +async function build(options: Options): Promise { + console.log(`Imma build ${options.output}`); +} + +async function run(cmd: string) { + try { + const { stdout } = await exec(cmd); + return stdout.trim(); + } catch (error) { + if (error.stderr) { + process.stderr.write(error.stderr); + } + throw new ExitCodeError(error.code, cmd); + } +} + +type Cache = { + // External location of the cache outside the output folder + location: string; + readable?: boolean; + writable?: boolean; + needsCopy?: boolean; + trees?: string[]; +}; + +async function readCache(options: Options): Promise { + const repoPath = relativePath(options.repoRoot, process.cwd()); + const location = resolvePath(options.cacheDir, repoPath); + + // Make sure we don't have any uncommitted changes to the input, in that case we consider the cache to be missing + try { + await exec(`git diff --quiet HEAD -- ${options.inputs.join(' ')}`); + } catch (error) { + return { location }; + } + + try { + const outputCacheExists = await fs.pathExists( + resolvePath(options.output, INFO_FILE), + ); + if (outputCacheExists) { + const trees = await readInfoFile(options.output); + if (trees) { + return { + location, + trees, + readable: true, + writable: true, + }; + } + } + + const externalCacheExists = await fs.pathExists(location); + if (externalCacheExists) { + const trees = await readInfoFile(location); + if (trees) { + return { + location, + trees, + readable: true, + writable: true, + needsCopy: true, + }; + } + } + } catch (error) { + console.log(`Cache not found, ${error}`); + } + return { location, writable: true }; +} + +async function readInfoFile(dir: string): Promise { + const infoContents = await fs.readFile(resolvePath(dir, INFO_FILE), 'utf-8'); + const { trees } = JSON.parse(infoContents); + return trees; +} + +async function getInputHashes(options: Options): Promise { + const trees = []; + for (const input of options.inputs) { + const output = await run(`git ls-tree HEAD '${input}'`); + const [, , sha] = output.split(/\s+/, 3); + trees.push(sha); + } + return trees; +} From d4fc4375641fc64d4a71ff4ec2e99bbdd007bc9d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 27 Mar 2020 11:23:04 +0100 Subject: [PATCH 119/146] cli/commands/build-cache: store external cache in tar archives --- packages/cli/package.json | 2 + .../cli/src/commands/build-cache/index.ts | 86 ++++++++++++++----- yarn.lock | 39 ++++++++- 3 files changed, 105 insertions(+), 22 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index f26b13a421..ba039dcd5e 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -32,10 +32,12 @@ "@types/ora": "^3.2.0", "@types/react-dev-utils": "^9.0.4", "@types/recursive-readdir": "^2.2.0", + "@types/tar": "^4.0.3", "@types/webpack": "^4.41.7", "@types/webpack-dev-server": "^3.10.0", "del": "^5.1.0", "nodemon": "^2.0.2", + "tar": "^6.0.1", "ts-node": "^8.6.2" }, "bin": { diff --git a/packages/cli/src/commands/build-cache/index.ts b/packages/cli/src/commands/build-cache/index.ts index 2d9a33d9f2..8138db54f7 100644 --- a/packages/cli/src/commands/build-cache/index.ts +++ b/packages/cli/src/commands/build-cache/index.ts @@ -15,14 +15,20 @@ */ import fs from 'fs-extra'; -import { resolve as resolvePath, relative as relativePath } from 'path'; +import { + dirname, + resolve as resolvePath, + relative as relativePath, +} from 'path'; import { promisify } from 'util'; import { exec as execCb } from 'child_process'; import { Command } from 'commander'; +import tar from 'tar'; import { ExitCodeError } from '../../helpers/errors'; const exec = promisify(execCb); const INFO_FILE = '.backstage-build-cache'; +const CACHE_ARCHIVE = 'cache.tgz'; type Options = { inputs: string[]; @@ -62,25 +68,29 @@ export default async (cmd: Command, args: string[]) => { console.log('DEBUG: trees =', trees); const cacheHit = cache.readable && cache.trees?.join(',') === trees.join(','); + console.log('DEBUG: cacheHit =', cacheHit); if (!cacheHit) { await build(options); - await fs.writeFile( - resolvePath(options.output, INFO_FILE), - JSON.stringify({ trees }, null, 2), - 'utf-8', - ); if (cache.writable) { + await fs.writeFile( + resolvePath(options.output, INFO_FILE), + JSON.stringify({ trees }, null, 2), + 'utf8', + ); console.log(`DEBUG: write cache`); - console.log('DEBUG: cache.location =', cache.location); - await fs.remove(cache.location); - console.log('DEBUG: options.output =', options.output); - await fs.copy(options.output, cache.location); + console.log('DEBUG: cache.archivePath =', cache.archivePath); + await fs.remove(cache.archivePath); + await fs.ensureDir(dirname(cache.archivePath)); + await tar.create( + { gzip: true, file: cache.archivePath, cwd: options.output }, + ['.'], + ); } } else if (cache.needsCopy) { - console.log(`DEBUG: would copy cache`); await fs.remove(options.output); - await fs.copy(cache.location, options.output); + await fs.ensureDir(options.output); + await tar.extract({ file: cache.archivePath, cwd: options.output }); } const ls = await run('ls derp'); @@ -105,7 +115,7 @@ async function run(cmd: string) { type Cache = { // External location of the cache outside the output folder - location: string; + archivePath: string; readable?: boolean; writable?: boolean; needsCopy?: boolean; @@ -115,12 +125,13 @@ type Cache = { async function readCache(options: Options): Promise { const repoPath = relativePath(options.repoRoot, process.cwd()); const location = resolvePath(options.cacheDir, repoPath); + const archivePath = resolvePath(location, CACHE_ARCHIVE); // Make sure we don't have any uncommitted changes to the input, in that case we consider the cache to be missing try { - await exec(`git diff --quiet HEAD -- ${options.inputs.join(' ')}`); + // await exec(`git diff --quiet HEAD -- ${options.inputs.join(' ')}`); } catch (error) { - return { location }; + return { archivePath }; } try { @@ -131,7 +142,7 @@ async function readCache(options: Options): Promise { const trees = await readInfoFile(options.output); if (trees) { return { - location, + archivePath, trees, readable: true, writable: true, @@ -141,10 +152,10 @@ async function readCache(options: Options): Promise { const externalCacheExists = await fs.pathExists(location); if (externalCacheExists) { - const trees = await readInfoFile(location); + const trees = await readInfoFileFromArchive(archivePath); if (trees) { return { - location, + archivePath, trees, readable: true, writable: true, @@ -155,15 +166,50 @@ async function readCache(options: Options): Promise { } catch (error) { console.log(`Cache not found, ${error}`); } - return { location, writable: true }; + return { archivePath, writable: true }; } async function readInfoFile(dir: string): Promise { - const infoContents = await fs.readFile(resolvePath(dir, INFO_FILE), 'utf-8'); + const infoContents = await fs.readFile(resolvePath(dir, INFO_FILE), 'utf8'); const { trees } = JSON.parse(infoContents); return trees; } +async function readInfoFileFromArchive( + archivePath: string, +): Promise { + const reader = fs.createReadStream(archivePath); + const parser = new ((tar.Parse as unknown) as { new (): tar.ParseStream })(); + + const infoEntry = await new Promise((resolve, reject) => { + parser.on('entry', entry => { + if (entry.path === `./${INFO_FILE}`) { + resolve(entry); + reader.close(); + } else { + entry.resume(); + } + }); + parser.on('end', () => { + reject(new Error('cache archive did not contain build info')); + }); + parser.on('error', error => reject(error)); + + reader.pipe(parser); + }); + + const infoData = await new Promise((resolve, reject) => { + const chunks = new Array(); + infoEntry.on('data', chunk => chunks.push(chunk)); + infoEntry.on('end', () => resolve(Buffer.concat(chunks))); + infoEntry.on('error', error => reject(error)); + }); + + const info = JSON.parse(infoData.toString('utf8')); + + return info.trees; +} + async function getInputHashes(options: Options): Promise { const trees = []; for (const input of options.inputs) { diff --git a/yarn.lock b/yarn.lock index dcde1de95a..db975122b5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3612,6 +3612,13 @@ resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== +"@types/minipass@*": + version "2.2.0" + resolved "https://registry.npmjs.org/@types/minipass/-/minipass-2.2.0.tgz#51ad404e8eb1fa961f75ec61205796807b6f9651" + integrity sha512-wuzZksN4w4kyfoOv/dlpov4NOunwutLA/q7uc00xU02ZyUY+aoM5PWIXEKBMnm0NHd4a+N71BMjq+x7+2Af1fg== + dependencies: + "@types/node" "*" + "@types/node@*", "@types/node@>= 8", "@types/node@^13.7.2": version "13.9.2" resolved "https://registry.npmjs.org/@types/node/-/node-13.9.2.tgz#ace1880c03594cc3e80206d96847157d8e7fa349" @@ -3776,6 +3783,14 @@ resolved "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.5.tgz#9adbc12950582aa65ead76bffdf39fe0c27a3c02" integrity sha512-/gG2M/Imw7cQFp8PGvz/SwocNrmKFjFsm5Pb8HdbHkZ1K8pmuPzOX4VeVoiEecFCVf4CsN1r3/BRvx+6sNqwtQ== +"@types/tar@^4.0.3": + version "4.0.3" + resolved "https://registry.npmjs.org/@types/tar/-/tar-4.0.3.tgz#e2cce0b8ff4f285293243f5971bd7199176ac489" + integrity sha512-Z7AVMMlkI8NTWF0qGhC4QIX0zkV/+y0J8x7b/RsHrN0310+YNjoJd8UrApCiGBCWtKjxS9QhNqLi2UJNToh5hA== + dependencies: + "@types/minipass" "*" + "@types/node" "*" + "@types/testing-library__cypress@^5.0.3": version "5.0.3" resolved "https://registry.npmjs.org/@types/testing-library__cypress/-/testing-library__cypress-5.0.3.tgz#94969b7c1eea96e09d8e023a1d225590fa75a1fe" @@ -5795,7 +5810,7 @@ chokidar@^3.2.2, chokidar@^3.3.0, chokidar@^3.3.1: optionalDependencies: fsevents "~2.1.2" -chownr@^1.1.1, chownr@^1.1.2, chownr@^1.1.4: +chownr@^1.1.1, chownr@^1.1.2, chownr@^1.1.3, chownr@^1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== @@ -13039,6 +13054,14 @@ minizlib@^1.2.1: dependencies: minipass "^2.9.0" +minizlib@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.0.tgz#fd52c645301ef09a63a2c209697c294c6ce02cf3" + integrity sha512-EzTZN/fjSvifSX0SlqUERCN39o6T40AMarPbv0MrarSFtIITCBh7bi+dU8nxGFHuqs9jdIAeoYoKuQAAASsPPA== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + mississippi@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" @@ -13078,7 +13101,7 @@ mkdirp-promise@^5.0.1: dependencies: mkdirp "*" -mkdirp@*: +mkdirp@*, mkdirp@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.3.tgz#4cf2e30ad45959dddea53ad97d518b6c8205e1ea" integrity sha512-6uCP4Qc0sWsgMLy1EOqqS/3rjDHOEnsStVr/4vtAIK2Y5i2kA7lFFejYrpIyiN9w0pYf4ckeCYT9f1r1P9KX5g== @@ -18083,6 +18106,18 @@ tar@^4.4.10, tar@^4.4.12, tar@^4.4.13, tar@^4.4.8: safe-buffer "^5.1.2" yallist "^3.0.3" +tar@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/tar/-/tar-6.0.1.tgz#7b3bd6c313cb6e0153770108f8d70ac298607efa" + integrity sha512-bKhKrrz2FJJj5s7wynxy/fyxpE0CmCjmOQ1KV4KkgXFWOgoIT/NbTMnB1n+LFNrNk0SSBVGGxcK5AGsyC+pW5Q== + dependencies: + chownr "^1.1.3" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.0" + mkdirp "^1.0.3" + yallist "^4.0.0" + telejson@^3.2.0: version "3.3.0" resolved "https://registry.npmjs.org/telejson/-/telejson-3.3.0.tgz#6d814f3c0d254d5c4770085aad063e266b56ad03" From 282af223519616e1ffba7db7c43159d3d95cda07 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 27 Mar 2020 11:38:46 +0100 Subject: [PATCH 120/146] cli/commands/build-cache: refactor + cleanup output --- .../cli/src/commands/build-cache/index.ts | 109 +++++++++--------- 1 file changed, 57 insertions(+), 52 deletions(-) diff --git a/packages/cli/src/commands/build-cache/index.ts b/packages/cli/src/commands/build-cache/index.ts index 8138db54f7..6d62159eeb 100644 --- a/packages/cli/src/commands/build-cache/index.ts +++ b/packages/cli/src/commands/build-cache/index.ts @@ -51,6 +51,10 @@ async function parseOptions(cmd: Command): Promise { return { inputs, output, cacheDir, repoRoot }; } +function print(msg: string) { + process.stdout.write(`[build-cache] ${msg}\n`); +} + /* * The build-cache command is used to make builds a no-op if there are no changes to the package. * It supports both local development where the output directory remains intact, as well as CI @@ -58,47 +62,54 @@ async function parseOptions(cmd: Command): Promise { */ export default async (cmd: Command, args: string[]) => { const options = await parseOptions(cmd); - console.log('DEBUG: options =', options); - console.log('DEBUG: args =', args); - const cache = await readCache(options); - console.log('DEBUG: cache =', cache); - const trees = await getInputHashes(options); - console.log('DEBUG: trees =', trees); const cacheHit = cache.readable && cache.trees?.join(',') === trees.join(','); - console.log('DEBUG: cacheHit =', cacheHit); - if (!cacheHit) { - await build(options); + if (cacheHit) { + print('cache hit, no need to build'); + + if (cache.needsCopy) { + print('copying external cache to build output dir'); + await copyFromExternalCache(cache, options); + } + } else { + print('cache miss, need to build'); + + await build(args); if (cache.writable) { - await fs.writeFile( - resolvePath(options.output, INFO_FILE), - JSON.stringify({ trees }, null, 2), - 'utf8', - ); - console.log(`DEBUG: write cache`); - console.log('DEBUG: cache.archivePath =', cache.archivePath); - await fs.remove(cache.archivePath); - await fs.ensureDir(dirname(cache.archivePath)); - await tar.create( - { gzip: true, file: cache.archivePath, cwd: options.output }, - ['.'], - ); + print('caching build output'); + const infoData = Buffer.from(JSON.stringify({ trees }, null, 2), 'utf8'); + await fs.writeFile(resolvePath(options.output, INFO_FILE), infoData); + await copyToExternalCache(cache, options); } - } else if (cache.needsCopy) { - await fs.remove(options.output); - await fs.ensureDir(options.output); - await tar.extract({ file: cache.archivePath, cwd: options.output }); } - - const ls = await run('ls derp'); - console.log('DEBUG: ls =', ls); }; -async function build(options: Options): Promise { - console.log(`Imma build ${options.output}`); +async function copyToExternalCache( + cache: Cache, + options: Options, +): Promise { + await fs.remove(cache.archivePath); + await fs.ensureDir(dirname(cache.archivePath)); + await tar.create( + { gzip: true, file: cache.archivePath, cwd: options.output }, + ['.'], + ); +} + +async function copyFromExternalCache( + cache: Cache, + options: Options, +): Promise { + await fs.remove(options.output); + await fs.ensureDir(options.output); + await tar.extract({ file: cache.archivePath, cwd: options.output }); +} + +async function build(args: string[]): Promise { + console.log(`Will build using '${args.join(' ')}'`); } async function run(cmd: string) { @@ -134,22 +145,22 @@ async function readCache(options: Options): Promise { return { archivePath }; } - try { - const outputCacheExists = await fs.pathExists( - resolvePath(options.output, INFO_FILE), - ); - if (outputCacheExists) { - const trees = await readInfoFile(options.output); - if (trees) { - return { - archivePath, - trees, - readable: true, - writable: true, - }; - } + const infoFilePath = resolvePath(options.output, INFO_FILE); + const outputCacheExists = await fs.pathExists(infoFilePath); + if (outputCacheExists) { + const infoData = await fs.readFile(infoFilePath); + const { trees } = JSON.parse(infoData.toString('utf8')); + if (trees) { + return { + archivePath, + trees, + readable: true, + writable: true, + }; } + } + try { const externalCacheExists = await fs.pathExists(location); if (externalCacheExists) { const trees = await readInfoFileFromArchive(archivePath); @@ -164,17 +175,11 @@ async function readCache(options: Options): Promise { } } } catch (error) { - console.log(`Cache not found, ${error}`); + print(`failed to read external cache archive, ${error}`); } return { archivePath, writable: true }; } -async function readInfoFile(dir: string): Promise { - const infoContents = await fs.readFile(resolvePath(dir, INFO_FILE), 'utf8'); - const { trees } = JSON.parse(infoContents); - return trees; -} - async function readInfoFileFromArchive( archivePath: string, ): Promise { From eaf80a4271d23d7736e490470cc44057cd1f8a48 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 27 Mar 2020 11:38:55 +0100 Subject: [PATCH 121/146] cli/commands/build-cache: fix unhandled error --- packages/cli/src/commands/build-cache/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/cli/src/commands/build-cache/index.ts b/packages/cli/src/commands/build-cache/index.ts index 6d62159eeb..5fe9852a28 100644 --- a/packages/cli/src/commands/build-cache/index.ts +++ b/packages/cli/src/commands/build-cache/index.ts @@ -199,6 +199,7 @@ async function readInfoFileFromArchive( reject(new Error('cache archive did not contain build info')); }); parser.on('error', error => reject(error)); + reader.on('error', error => reject(error)); reader.pipe(parser); }); From 4e79446dc91e2ff84fe52aa79e16f501bb30b7bf Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 27 Mar 2020 11:49:32 +0100 Subject: [PATCH 122/146] cli/commands/build-cache: run build command --- packages/cli/src/commands/build-cache/index.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/cli/src/commands/build-cache/index.ts b/packages/cli/src/commands/build-cache/index.ts index 5fe9852a28..6ae1844412 100644 --- a/packages/cli/src/commands/build-cache/index.ts +++ b/packages/cli/src/commands/build-cache/index.ts @@ -25,6 +25,7 @@ import { exec as execCb } from 'child_process'; import { Command } from 'commander'; import tar from 'tar'; import { ExitCodeError } from '../../helpers/errors'; +import { run } from '../../helpers/run'; const exec = promisify(execCb); const INFO_FILE = '.backstage-build-cache'; @@ -38,7 +39,7 @@ type Options = { }; async function parseOptions(cmd: Command): Promise { - const repoRoot = await run('git rev-parse --show-toplevel'); + const repoRoot = await cmdOutput('git rev-parse --show-toplevel'); const argTransformer = (arg: string) => resolvePath(arg.replace(//g, repoRoot).replace(/'/g, '')); @@ -67,11 +68,11 @@ export default async (cmd: Command, args: string[]) => { const cacheHit = cache.readable && cache.trees?.join(',') === trees.join(','); if (cacheHit) { - print('cache hit, no need to build'); - if (cache.needsCopy) { - print('copying external cache to build output dir'); + print('external cache hit, copying from external cache'); await copyFromExternalCache(cache, options); + } else { + print('cache hit, nothing to be done'); } } else { print('cache miss, need to build'); @@ -108,11 +109,11 @@ async function copyFromExternalCache( await tar.extract({ file: cache.archivePath, cwd: options.output }); } -async function build(args: string[]): Promise { - console.log(`Will build using '${args.join(' ')}'`); +async function build([prog, ...args]: string[]): Promise { + await run(prog, args); } -async function run(cmd: string) { +async function cmdOutput(cmd: string) { try { const { stdout } = await exec(cmd); return stdout.trim(); @@ -219,7 +220,7 @@ async function readInfoFileFromArchive( async function getInputHashes(options: Options): Promise { const trees = []; for (const input of options.inputs) { - const output = await run(`git ls-tree HEAD '${input}'`); + const output = await cmdOutput(`git ls-tree HEAD '${input}'`); const [, , sha] = output.split(/\s+/, 3); trees.push(sha); } From 2dae5fccafcb924e37580ca40a1252e80b0cd032 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 27 Mar 2020 12:00:54 +0100 Subject: [PATCH 123/146] cli/commands/build-cache: move archive handling to separate module --- .../cli/src/commands/build-cache/archive.ts | 74 +++++++++++++++++++ .../cli/src/commands/build-cache/index.ts | 72 ++---------------- 2 files changed, 80 insertions(+), 66 deletions(-) create mode 100644 packages/cli/src/commands/build-cache/archive.ts diff --git a/packages/cli/src/commands/build-cache/archive.ts b/packages/cli/src/commands/build-cache/archive.ts new file mode 100644 index 0000000000..be74504f1f --- /dev/null +++ b/packages/cli/src/commands/build-cache/archive.ts @@ -0,0 +1,74 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs-extra'; +import tar from 'tar'; +import { dirname } from 'path'; + +export async function readFileFromArchive( + archivePath: string, + filePath: string, +): Promise { + const reader = fs.createReadStream(archivePath); + const parser = new ((tar.Parse as unknown) as { new (): tar.ParseStream })(); + + const fileEntry = await new Promise((resolve, reject) => { + parser.on('entry', entry => { + if (entry.path === `./${filePath}`) { + resolve(entry); + reader.close(); + } else { + entry.resume(); + } + }); + parser.on('end', () => { + reject(new Error('cache archive did not contain build info')); + }); + parser.on('error', error => reject(error)); + reader.on('error', error => reject(error)); + + reader.pipe(parser); + }); + + const data = await new Promise((resolve, reject) => { + const chunks = new Array(); + fileEntry.on('data', chunk => chunks.push(chunk)); + fileEntry.on('end', () => resolve(Buffer.concat(chunks))); + fileEntry.on('error', error => reject(error)); + }); + + return data; +} + +// packages all files in inputDir into an archive at archivePath, deleting any existing archive +export async function createArchive( + archivePath: string, + inputDir: string, +): Promise { + await fs.remove(archivePath); + await fs.ensureDir(dirname(archivePath)); + await tar.create({ gzip: true, file: archivePath, cwd: inputDir }, ['.']); +} + +// extracts archive at archive path into outputDir, deleting any existing files at outputDir +export async function extractArchive( + archivePath: string, + outputDir: string, +): Promise { + await fs.remove(outputDir); + await fs.ensureDir(outputDir); + await tar.extract({ file: archivePath, cwd: outputDir }); +} diff --git a/packages/cli/src/commands/build-cache/index.ts b/packages/cli/src/commands/build-cache/index.ts index 6ae1844412..49a02f6939 100644 --- a/packages/cli/src/commands/build-cache/index.ts +++ b/packages/cli/src/commands/build-cache/index.ts @@ -15,17 +15,13 @@ */ import fs from 'fs-extra'; -import { - dirname, - resolve as resolvePath, - relative as relativePath, -} from 'path'; +import { resolve as resolvePath, relative as relativePath } from 'path'; import { promisify } from 'util'; import { exec as execCb } from 'child_process'; import { Command } from 'commander'; -import tar from 'tar'; import { ExitCodeError } from '../../helpers/errors'; import { run } from '../../helpers/run'; +import { readFileFromArchive, extractArchive, createArchive } from './archive'; const exec = promisify(execCb); const INFO_FILE = '.backstage-build-cache'; @@ -70,7 +66,7 @@ export default async (cmd: Command, args: string[]) => { if (cacheHit) { if (cache.needsCopy) { print('external cache hit, copying from external cache'); - await copyFromExternalCache(cache, options); + await extractArchive(cache.archivePath, options.output); } else { print('cache hit, nothing to be done'); } @@ -83,32 +79,11 @@ export default async (cmd: Command, args: string[]) => { print('caching build output'); const infoData = Buffer.from(JSON.stringify({ trees }, null, 2), 'utf8'); await fs.writeFile(resolvePath(options.output, INFO_FILE), infoData); - await copyToExternalCache(cache, options); + await createArchive(cache.archivePath, options.output); } } }; -async function copyToExternalCache( - cache: Cache, - options: Options, -): Promise { - await fs.remove(cache.archivePath); - await fs.ensureDir(dirname(cache.archivePath)); - await tar.create( - { gzip: true, file: cache.archivePath, cwd: options.output }, - ['.'], - ); -} - -async function copyFromExternalCache( - cache: Cache, - options: Options, -): Promise { - await fs.remove(options.output); - await fs.ensureDir(options.output); - await tar.extract({ file: cache.archivePath, cwd: options.output }); -} - async function build([prog, ...args]: string[]): Promise { await run(prog, args); } @@ -164,7 +139,8 @@ async function readCache(options: Options): Promise { try { const externalCacheExists = await fs.pathExists(location); if (externalCacheExists) { - const trees = await readInfoFileFromArchive(archivePath); + const infoData = await readFileFromArchive(archivePath, INFO_FILE); + const { trees } = JSON.parse(infoData.toString('utf8')); if (trees) { return { archivePath, @@ -181,42 +157,6 @@ async function readCache(options: Options): Promise { return { archivePath, writable: true }; } -async function readInfoFileFromArchive( - archivePath: string, -): Promise { - const reader = fs.createReadStream(archivePath); - const parser = new ((tar.Parse as unknown) as { new (): tar.ParseStream })(); - - const infoEntry = await new Promise((resolve, reject) => { - parser.on('entry', entry => { - if (entry.path === `./${INFO_FILE}`) { - resolve(entry); - reader.close(); - } else { - entry.resume(); - } - }); - parser.on('end', () => { - reject(new Error('cache archive did not contain build info')); - }); - parser.on('error', error => reject(error)); - reader.on('error', error => reject(error)); - - reader.pipe(parser); - }); - - const infoData = await new Promise((resolve, reject) => { - const chunks = new Array(); - infoEntry.on('data', chunk => chunks.push(chunk)); - infoEntry.on('end', () => resolve(Buffer.concat(chunks))); - infoEntry.on('error', error => reject(error)); - }); - - const info = JSON.parse(infoData.toString('utf8')); - - return info.trees; -} - async function getInputHashes(options: Options): Promise { const trees = []; for (const input of options.inputs) { From 0644f47645bcaf3f9675b3ecdd8c8675b9c75d22 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 27 Mar 2020 12:07:31 +0100 Subject: [PATCH 124/146] cli/commands/build-cache: simplify build and move cmdOutput to helpers as runPlain --- .../cli/src/commands/build-cache/index.ts | 28 +++---------------- packages/cli/src/helpers/run.ts | 21 +++++++++++++- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/packages/cli/src/commands/build-cache/index.ts b/packages/cli/src/commands/build-cache/index.ts index 49a02f6939..b01b5309ab 100644 --- a/packages/cli/src/commands/build-cache/index.ts +++ b/packages/cli/src/commands/build-cache/index.ts @@ -16,13 +16,9 @@ import fs from 'fs-extra'; import { resolve as resolvePath, relative as relativePath } from 'path'; -import { promisify } from 'util'; -import { exec as execCb } from 'child_process'; import { Command } from 'commander'; -import { ExitCodeError } from '../../helpers/errors'; -import { run } from '../../helpers/run'; +import { run, runPlain } from '../../helpers/run'; import { readFileFromArchive, extractArchive, createArchive } from './archive'; -const exec = promisify(execCb); const INFO_FILE = '.backstage-build-cache'; const CACHE_ARCHIVE = 'cache.tgz'; @@ -35,7 +31,7 @@ type Options = { }; async function parseOptions(cmd: Command): Promise { - const repoRoot = await cmdOutput('git rev-parse --show-toplevel'); + const repoRoot = await runPlain('git rev-parse --show-toplevel'); const argTransformer = (arg: string) => resolvePath(arg.replace(//g, repoRoot).replace(/'/g, '')); @@ -73,7 +69,7 @@ export default async (cmd: Command, args: string[]) => { } else { print('cache miss, need to build'); - await build(args); + await run(args[0], args.slice(1)); if (cache.writable) { print('caching build output'); @@ -84,22 +80,6 @@ export default async (cmd: Command, args: string[]) => { } }; -async function build([prog, ...args]: string[]): Promise { - await run(prog, args); -} - -async function cmdOutput(cmd: string) { - try { - const { stdout } = await exec(cmd); - return stdout.trim(); - } catch (error) { - if (error.stderr) { - process.stderr.write(error.stderr); - } - throw new ExitCodeError(error.code, cmd); - } -} - type Cache = { // External location of the cache outside the output folder archivePath: string; @@ -160,7 +140,7 @@ async function readCache(options: Options): Promise { async function getInputHashes(options: Options): Promise { const trees = []; for (const input of options.inputs) { - const output = await cmdOutput(`git ls-tree HEAD '${input}'`); + const output = await runPlain(`git ls-tree HEAD '${input}'`); const [, , sha] = output.split(/\s+/, 3); trees.push(sha); } diff --git a/packages/cli/src/helpers/run.ts b/packages/cli/src/helpers/run.ts index 3eec19a148..6a1376baf8 100644 --- a/packages/cli/src/helpers/run.ts +++ b/packages/cli/src/helpers/run.ts @@ -14,8 +14,15 @@ * limitations under the License. */ -import { SpawnOptions, spawn, ChildProcess } from 'child_process'; +import { + SpawnOptions, + spawn, + ChildProcess, + exec as execCb, +} from 'child_process'; import { ExitCodeError } from './errors'; +import { promisify } from 'util'; +const exec = promisify(execCb); type SpawnOptionsPartialEnv = Omit & { env?: Partial; @@ -43,6 +50,18 @@ export async function run( await waitForExit(child, name); } +export async function runPlain(cmd: string) { + try { + const { stdout } = await exec(cmd); + return stdout.trim(); + } catch (error) { + if (error.stderr) { + process.stderr.write(error.stderr); + } + throw new ExitCodeError(error.code, cmd); + } +} + export async function waitForExit( child: ChildProcess & { exitCode?: number }, name?: string, From 8a35da15e5449151440f66cbeea06eae555c580a Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 27 Mar 2020 12:15:19 +0100 Subject: [PATCH 125/146] cli/commands/build-cache: separate out cache and options handling --- .../cli/src/commands/build-cache/cache.ts | 99 +++++++++++++++++ .../cli/src/commands/build-cache/index.ts | 104 +----------------- .../cli/src/commands/build-cache/options.ts | 40 +++++++ 3 files changed, 145 insertions(+), 98 deletions(-) create mode 100644 packages/cli/src/commands/build-cache/cache.ts create mode 100644 packages/cli/src/commands/build-cache/options.ts diff --git a/packages/cli/src/commands/build-cache/cache.ts b/packages/cli/src/commands/build-cache/cache.ts new file mode 100644 index 0000000000..67030e6eb2 --- /dev/null +++ b/packages/cli/src/commands/build-cache/cache.ts @@ -0,0 +1,99 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs-extra'; +import { resolve as resolvePath, relative as relativePath } from 'path'; +import { runPlain } from '../../helpers/run'; +import { readFileFromArchive } from './archive'; +import { Options } from './options'; + +export type Cache = { + // External location of the cache outside the output folder + archivePath: string; + readable?: boolean; + writable?: boolean; + needsCopy?: boolean; + trees?: string[]; +}; + +const CACHE_ARCHIVE = 'cache.tgz'; +const INFO_FILE = '.backstage-build-cache'; + +export async function readCache(options: Options): Promise { + const repoPath = relativePath(options.repoRoot, process.cwd()); + const location = resolvePath(options.cacheDir, repoPath); + const archivePath = resolvePath(location, CACHE_ARCHIVE); + + // Make sure we don't have any uncommitted changes to the input, in that case we consider the cache to be missing + try { + // await exec(`git diff --quiet HEAD -- ${options.inputs.join(' ')}`); + } catch (error) { + return { archivePath }; + } + + const infoFilePath = resolvePath(options.output, INFO_FILE); + const outputCacheExists = await fs.pathExists(infoFilePath); + if (outputCacheExists) { + const infoData = await fs.readFile(infoFilePath); + const { trees } = JSON.parse(infoData.toString('utf8')); + if (trees) { + return { + archivePath, + trees, + readable: true, + writable: true, + }; + } + } + + try { + const externalCacheExists = await fs.pathExists(location); + if (externalCacheExists) { + const infoData = await readFileFromArchive(archivePath, INFO_FILE); + const { trees } = JSON.parse(infoData.toString('utf8')); + if (trees) { + return { + archivePath, + trees, + readable: true, + writable: true, + needsCopy: true, + }; + } + } + } catch (error) { + throw new Error(`failed to read external cache archive, ${error}`); + } + return { archivePath, writable: true }; +} + +export async function writeCacheInfo( + trees: string[], + options: Options, +): Promise { + const infoData = Buffer.from(JSON.stringify({ trees }, null, 2), 'utf8'); + await fs.writeFile(resolvePath(options.output, INFO_FILE), infoData); +} + +export async function readInputHashes(options: Options): Promise { + const trees = []; + for (const input of options.inputs) { + const output = await runPlain(`git ls-tree HEAD '${input}'`); + const [, , sha] = output.split(/\s+/, 3); + trees.push(sha); + } + return trees; +} diff --git a/packages/cli/src/commands/build-cache/index.ts b/packages/cli/src/commands/build-cache/index.ts index b01b5309ab..07bd6c89b9 100644 --- a/packages/cli/src/commands/build-cache/index.ts +++ b/packages/cli/src/commands/build-cache/index.ts @@ -14,35 +14,11 @@ * limitations under the License. */ -import fs from 'fs-extra'; -import { resolve as resolvePath, relative as relativePath } from 'path'; import { Command } from 'commander'; -import { run, runPlain } from '../../helpers/run'; -import { readFileFromArchive, extractArchive, createArchive } from './archive'; - -const INFO_FILE = '.backstage-build-cache'; -const CACHE_ARCHIVE = 'cache.tgz'; - -type Options = { - inputs: string[]; - output: string; - cacheDir: string; - repoRoot: string; -}; - -async function parseOptions(cmd: Command): Promise { - const repoRoot = await runPlain('git rev-parse --show-toplevel'); - const argTransformer = (arg: string) => - resolvePath(arg.replace(//g, repoRoot).replace(/'/g, '')); - - const inputs = cmd.input.map(argTransformer) as string[]; - if (inputs.length === 0) { - inputs.push(argTransformer('.')); - } - const output = argTransformer(cmd.output); - const cacheDir = argTransformer(cmd.cacheDir); - return { inputs, output, cacheDir, repoRoot }; -} +import { run } from '../../helpers/run'; +import { extractArchive, createArchive } from './archive'; +import { readCache, readInputHashes, writeCacheInfo } from './cache'; +import { parseOptions } from './options'; function print(msg: string) { process.stdout.write(`[build-cache] ${msg}\n`); @@ -56,7 +32,7 @@ function print(msg: string) { export default async (cmd: Command, args: string[]) => { const options = await parseOptions(cmd); const cache = await readCache(options); - const trees = await getInputHashes(options); + const trees = await readInputHashes(options); const cacheHit = cache.readable && cache.trees?.join(',') === trees.join(','); if (cacheHit) { @@ -73,76 +49,8 @@ export default async (cmd: Command, args: string[]) => { if (cache.writable) { print('caching build output'); - const infoData = Buffer.from(JSON.stringify({ trees }, null, 2), 'utf8'); - await fs.writeFile(resolvePath(options.output, INFO_FILE), infoData); + await writeCacheInfo(trees, options); await createArchive(cache.archivePath, options.output); } } }; - -type Cache = { - // External location of the cache outside the output folder - archivePath: string; - readable?: boolean; - writable?: boolean; - needsCopy?: boolean; - trees?: string[]; -}; - -async function readCache(options: Options): Promise { - const repoPath = relativePath(options.repoRoot, process.cwd()); - const location = resolvePath(options.cacheDir, repoPath); - const archivePath = resolvePath(location, CACHE_ARCHIVE); - - // Make sure we don't have any uncommitted changes to the input, in that case we consider the cache to be missing - try { - // await exec(`git diff --quiet HEAD -- ${options.inputs.join(' ')}`); - } catch (error) { - return { archivePath }; - } - - const infoFilePath = resolvePath(options.output, INFO_FILE); - const outputCacheExists = await fs.pathExists(infoFilePath); - if (outputCacheExists) { - const infoData = await fs.readFile(infoFilePath); - const { trees } = JSON.parse(infoData.toString('utf8')); - if (trees) { - return { - archivePath, - trees, - readable: true, - writable: true, - }; - } - } - - try { - const externalCacheExists = await fs.pathExists(location); - if (externalCacheExists) { - const infoData = await readFileFromArchive(archivePath, INFO_FILE); - const { trees } = JSON.parse(infoData.toString('utf8')); - if (trees) { - return { - archivePath, - trees, - readable: true, - writable: true, - needsCopy: true, - }; - } - } - } catch (error) { - print(`failed to read external cache archive, ${error}`); - } - return { archivePath, writable: true }; -} - -async function getInputHashes(options: Options): Promise { - const trees = []; - for (const input of options.inputs) { - const output = await runPlain(`git ls-tree HEAD '${input}'`); - const [, , sha] = output.split(/\s+/, 3); - trees.push(sha); - } - return trees; -} diff --git a/packages/cli/src/commands/build-cache/options.ts b/packages/cli/src/commands/build-cache/options.ts new file mode 100644 index 0000000000..8df62afaa5 --- /dev/null +++ b/packages/cli/src/commands/build-cache/options.ts @@ -0,0 +1,40 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { resolve as resolvePath } from 'path'; +import { Command } from 'commander'; +import { runPlain } from '../../helpers/run'; + +export type Options = { + inputs: string[]; + output: string; + cacheDir: string; + repoRoot: string; +}; + +export async function parseOptions(cmd: Command): Promise { + const repoRoot = await runPlain('git rev-parse --show-toplevel'); + const argTransformer = (arg: string) => + resolvePath(arg.replace(//g, repoRoot).replace(/'/g, '')); + + const inputs = cmd.input.map(argTransformer) as string[]; + if (inputs.length === 0) { + inputs.push(argTransformer('.')); + } + const output = argTransformer(cmd.output); + const cacheDir = argTransformer(cmd.cacheDir); + return { inputs, output, cacheDir, repoRoot }; +} From 39efe52c9dab6bdf782ed10e9de3d69e3996d151 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 27 Mar 2020 13:04:53 +0100 Subject: [PATCH 126/146] cli/commands/build-cache: refactor cache handling --- .../cli/src/commands/build-cache/cache.ts | 144 +++++++++++------- .../cli/src/commands/build-cache/index.ts | 18 +-- 2 files changed, 98 insertions(+), 64 deletions(-) diff --git a/packages/cli/src/commands/build-cache/cache.ts b/packages/cli/src/commands/build-cache/cache.ts index 67030e6eb2..b4123f017f 100644 --- a/packages/cli/src/commands/build-cache/cache.ts +++ b/packages/cli/src/commands/build-cache/cache.ts @@ -20,7 +20,7 @@ import { runPlain } from '../../helpers/run'; import { readFileFromArchive } from './archive'; import { Options } from './options'; -export type Cache = { +export type CacheHit = { // External location of the cache outside the output folder archivePath: string; readable?: boolean; @@ -29,71 +29,105 @@ export type Cache = { trees?: string[]; }; +export type CacheKey = string[]; + +type CacheEntry = { + key: CacheKey; + archivePath: string; +}; + const CACHE_ARCHIVE = 'cache.tgz'; const INFO_FILE = '.backstage-build-cache'; -export async function readCache(options: Options): Promise { - const repoPath = relativePath(options.repoRoot, process.cwd()); - const location = resolvePath(options.cacheDir, repoPath); - const archivePath = resolvePath(location, CACHE_ARCHIVE); +export class Cache { + static async read(options: Options) { + const repoPath = relativePath(options.repoRoot, process.cwd()); + const location = resolvePath(options.cacheDir, repoPath); - // Make sure we don't have any uncommitted changes to the input, in that case we consider the cache to be missing - try { - // await exec(`git diff --quiet HEAD -- ${options.inputs.join(' ')}`); - } catch (error) { - return { archivePath }; + // Make sure we don't have any uncommitted changes to the input, in that case we consider the cache to be missing + try { + // await exec(`git diff --quiet HEAD -- ${options.inputs.join(' ')}`); + } catch (error) { + return new Cache(); + } + + let localKey: CacheKey = []; + + const localInfoFile = resolvePath(options.output, INFO_FILE); + const outputCacheExists = await fs.pathExists(localInfoFile); + if (outputCacheExists) { + const infoData = await fs.readFile(localInfoFile); + const { trees } = JSON.parse(infoData.toString('utf8')); + localKey = trees; + } + + try { + const externalCacheExists = await fs.pathExists(location); + if (externalCacheExists) { + const archivePath = resolvePath(location, CACHE_ARCHIVE); + const infoData = await readFileFromArchive(archivePath, INFO_FILE); + const { trees } = JSON.parse(infoData.toString('utf8')); + if (trees) { + return new Cache([{ archivePath, key: trees }], location, localKey); + } + } + } catch (error) { + throw new Error(`failed to read external cache archive, ${error}`); + } + + return new Cache([], location, localKey); } - const infoFilePath = resolvePath(options.output, INFO_FILE); - const outputCacheExists = await fs.pathExists(infoFilePath); - if (outputCacheExists) { - const infoData = await fs.readFile(infoFilePath); - const { trees } = JSON.parse(infoData.toString('utf8')); - if (trees) { + static async readInputKey(inputPaths: string[]): Promise { + const trees = []; + for (const inputPath of inputPaths) { + const output = await runPlain(`git ls-tree HEAD '${inputPath}'`); + const [, , sha] = output.split(/\s+/, 3); + trees.push(sha); + } + return trees; + } + + constructor( + private readonly entries: CacheEntry[] = [], + private readonly location?: string, + private readonly localKey?: CacheKey, + ) {} + + find(key: CacheKey): CacheHit | undefined { + if (!this.location) { + return undefined; + } + if (this.localKey?.join(',') === key.join(',')) { return { - archivePath, - trees, - readable: true, - writable: true, + needsCopy: false, + archivePath: resolvePath(this.location, CACHE_ARCHIVE), }; } - } - - try { - const externalCacheExists = await fs.pathExists(location); - if (externalCacheExists) { - const infoData = await readFileFromArchive(archivePath, INFO_FILE); - const { trees } = JSON.parse(infoData.toString('utf8')); - if (trees) { - return { - archivePath, - trees, - readable: true, - writable: true, - needsCopy: true, - }; - } + const matchingEntry = this.entries.find(entry => entry.key === key); + if (!matchingEntry) { + return undefined; } - } catch (error) { - throw new Error(`failed to read external cache archive, ${error}`); + return { + needsCopy: true, + archivePath: matchingEntry.archivePath, + }; } - return { archivePath, writable: true }; -} -export async function writeCacheInfo( - trees: string[], - options: Options, -): Promise { - const infoData = Buffer.from(JSON.stringify({ trees }, null, 2), 'utf8'); - await fs.writeFile(resolvePath(options.output, INFO_FILE), infoData); -} - -export async function readInputHashes(options: Options): Promise { - const trees = []; - for (const input of options.inputs) { - const output = await runPlain(`git ls-tree HEAD '${input}'`); - const [, , sha] = output.split(/\s+/, 3); - trees.push(sha); + get shouldCacheOutput() { + return Boolean(this.location); + } + + async prepareOutput(key: CacheKey, outputDir: string): Promise { + if (!this.location) { + throw new Error("can't write cache output, no location set"); + } + const infoData = Buffer.from( + JSON.stringify({ trees: key }, null, 2), + 'utf8', + ); + await fs.writeFile(resolvePath(outputDir, INFO_FILE), infoData); + const archivePath = resolvePath(this.location, CACHE_ARCHIVE); + return archivePath; } - return trees; } diff --git a/packages/cli/src/commands/build-cache/index.ts b/packages/cli/src/commands/build-cache/index.ts index 07bd6c89b9..003560c6bd 100644 --- a/packages/cli/src/commands/build-cache/index.ts +++ b/packages/cli/src/commands/build-cache/index.ts @@ -17,7 +17,7 @@ import { Command } from 'commander'; import { run } from '../../helpers/run'; import { extractArchive, createArchive } from './archive'; -import { readCache, readInputHashes, writeCacheInfo } from './cache'; +import { Cache } from './cache'; import { parseOptions } from './options'; function print(msg: string) { @@ -31,14 +31,14 @@ function print(msg: string) { */ export default async (cmd: Command, args: string[]) => { const options = await parseOptions(cmd); - const cache = await readCache(options); - const trees = await readInputHashes(options); + const cache = await Cache.read(options); + const key = await Cache.readInputKey(options.inputs); - const cacheHit = cache.readable && cache.trees?.join(',') === trees.join(','); + const cacheHit = cache.find(key); if (cacheHit) { - if (cache.needsCopy) { + if (cacheHit.needsCopy) { print('external cache hit, copying from external cache'); - await extractArchive(cache.archivePath, options.output); + await extractArchive(cacheHit.archivePath, options.output); } else { print('cache hit, nothing to be done'); } @@ -47,10 +47,10 @@ export default async (cmd: Command, args: string[]) => { await run(args[0], args.slice(1)); - if (cache.writable) { + if (cache.shouldCacheOutput) { print('caching build output'); - await writeCacheInfo(trees, options); - await createArchive(cache.archivePath, options.output); + const archivePath = await cache.prepareOutput(key, options.output); + await createArchive(archivePath, options.output); } } }; From f2d7d43b77ac6e0a2726a37bbd294e33f5e2f8ca Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 27 Mar 2020 13:56:51 +0100 Subject: [PATCH 127/146] cli/commands/build-cache: add support for multiple cache archives --- .../cli/src/commands/build-cache/cache.ts | 167 +++++++++++------- .../cli/src/commands/build-cache/index.ts | 16 +- 2 files changed, 110 insertions(+), 73 deletions(-) diff --git a/packages/cli/src/commands/build-cache/cache.ts b/packages/cli/src/commands/build-cache/cache.ts index b4123f017f..d5ed39e148 100644 --- a/packages/cli/src/commands/build-cache/cache.ts +++ b/packages/cli/src/commands/build-cache/cache.ts @@ -17,27 +17,29 @@ import fs from 'fs-extra'; import { resolve as resolvePath, relative as relativePath } from 'path'; import { runPlain } from '../../helpers/run'; -import { readFileFromArchive } from './archive'; import { Options } from './options'; +import { extractArchive, createArchive } from './archive'; -export type CacheHit = { - // External location of the cache outside the output folder - archivePath: string; - readable?: boolean; - writable?: boolean; - needsCopy?: boolean; - trees?: string[]; +const MAX_ENTRIES = 10; +const INFO_FILE = '.backstage-build-cache'; + +export type CacheQueryResult = { + hit: boolean; + copy?: (outputDir: string) => Promise; + archive?: (outputDir: string) => Promise; }; export type CacheKey = string[]; type CacheEntry = { key: CacheKey; - archivePath: string; + path: string; }; -const CACHE_ARCHIVE = 'cache.tgz'; -const INFO_FILE = '.backstage-build-cache'; +type CacheInfo = { + key?: CacheKey; + entries?: CacheEntry[]; +}; export class Cache { static async read(options: Options) { @@ -51,31 +53,11 @@ export class Cache { return new Cache(); } - let localKey: CacheKey = []; + const outputInfo = await readCacheInfo(options.output); + const localKey = outputInfo?.key; - const localInfoFile = resolvePath(options.output, INFO_FILE); - const outputCacheExists = await fs.pathExists(localInfoFile); - if (outputCacheExists) { - const infoData = await fs.readFile(localInfoFile); - const { trees } = JSON.parse(infoData.toString('utf8')); - localKey = trees; - } - - try { - const externalCacheExists = await fs.pathExists(location); - if (externalCacheExists) { - const archivePath = resolvePath(location, CACHE_ARCHIVE); - const infoData = await readFileFromArchive(archivePath, INFO_FILE); - const { trees } = JSON.parse(infoData.toString('utf8')); - if (trees) { - return new Cache([{ archivePath, key: trees }], location, localKey); - } - } - } catch (error) { - throw new Error(`failed to read external cache archive, ${error}`); - } - - return new Cache([], location, localKey); + const { entries = [] } = (await readCacheInfo(location)) ?? {}; + return new Cache(entries, location, localKey); } static async readInputKey(inputPaths: string[]): Promise { @@ -94,40 +76,97 @@ export class Cache { private readonly localKey?: CacheKey, ) {} - find(key: CacheKey): CacheHit | undefined { - if (!this.location) { - return undefined; + query(key: CacheKey): CacheQueryResult { + const { location } = this; + if (!location) { + return { hit: false }; } - if (this.localKey?.join(',') === key.join(',')) { - return { - needsCopy: false, - archivePath: resolvePath(this.location, CACHE_ARCHIVE), - }; + + const archive = async (outputDir: string) => { + await writeCacheInfo(outputDir, { key }); + + const timestamp = new Date().toISOString().replace(/-|:|\..*/g, ''); + const rand = Math.random() + .toString(36) + .slice(2, 6); + const archiveName = `cache-${timestamp}-${rand}.tgz`; + const archivePath = resolvePath(location, archiveName); + + // Read existing entries and prepend the new one + const { entries = [] } = (await readCacheInfo(location)) ?? {}; + + // Check if there's already aan entry for this key, in that case we just wanna bump it + const entryIndex = entries.findIndex(e => compareKeys(e.key, key)); + if (entryIndex !== -1) { + const [existingEntry] = entries.splice(entryIndex, 1); + entries.unshift(existingEntry); + + await writeCacheInfo(location, { entries }); + return; + } + + // Create and add new archive to entries + await createArchive(archivePath, outputDir); + entries.unshift({ key, path: archiveName }); + + // Remove old cache entries + const removedEntries = entries.splice(MAX_ENTRIES); + for (const entry of removedEntries) { + try { + await fs.remove(resolvePath(location, entry.path)); + } catch (error) { + process.stderr.write(`failed to remove old cache entry, ${error}\n`); + } + } + + await writeCacheInfo(location, { entries }); + }; + + if (compareKeys(this.localKey, key)) { + return { hit: true, archive }; } - const matchingEntry = this.entries.find(entry => entry.key === key); + + const matchingEntry = this.entries.find(e => compareKeys(e.key, key)); if (!matchingEntry) { - return undefined; + return { hit: false, archive }; } + return { - needsCopy: true, - archivePath: matchingEntry.archivePath, + hit: true, + archive, + copy: async (outputDir: string) => { + const archivePath = resolvePath(location, matchingEntry.path); + await extractArchive(archivePath, outputDir); + }, }; } - - get shouldCacheOutput() { - return Boolean(this.location); - } - - async prepareOutput(key: CacheKey, outputDir: string): Promise { - if (!this.location) { - throw new Error("can't write cache output, no location set"); - } - const infoData = Buffer.from( - JSON.stringify({ trees: key }, null, 2), - 'utf8', - ); - await fs.writeFile(resolvePath(outputDir, INFO_FILE), infoData); - const archivePath = resolvePath(this.location, CACHE_ARCHIVE); - return archivePath; - } +} + +function compareKeys(a?: CacheKey, b?: CacheKey): boolean { + if (!a || !b) { + return false; + } + return a.join(',') === b.join(','); +} + +async function readCacheInfo( + parentDir: string, +): Promise { + const infoFile = resolvePath(parentDir, INFO_FILE); + const exists = await fs.pathExists(infoFile); + if (!exists) { + return undefined; + } + + const infoData = await fs.readFile(infoFile); + const cacheInfo = JSON.parse(infoData.toString('utf8')) as CacheInfo; + return cacheInfo; +} + +async function writeCacheInfo( + parentDir: string, + cacheInfo: CacheInfo, +): Promise { + const infoData = Buffer.from(JSON.stringify(cacheInfo, null, 2), 'utf8'); + await fs.writeFile(resolvePath(parentDir, INFO_FILE), infoData); } diff --git a/packages/cli/src/commands/build-cache/index.ts b/packages/cli/src/commands/build-cache/index.ts index 003560c6bd..2aa2911116 100644 --- a/packages/cli/src/commands/build-cache/index.ts +++ b/packages/cli/src/commands/build-cache/index.ts @@ -16,7 +16,6 @@ import { Command } from 'commander'; import { run } from '../../helpers/run'; -import { extractArchive, createArchive } from './archive'; import { Cache } from './cache'; import { parseOptions } from './options'; @@ -34,11 +33,11 @@ export default async (cmd: Command, args: string[]) => { const cache = await Cache.read(options); const key = await Cache.readInputKey(options.inputs); - const cacheHit = cache.find(key); - if (cacheHit) { - if (cacheHit.needsCopy) { - print('external cache hit, copying from external cache'); - await extractArchive(cacheHit.archivePath, options.output); + const cacheResult = cache.query(key); + if (cacheResult.hit) { + if (cacheResult.copy) { + print('external cache hit, copying archive to output folder'); + await cacheResult.copy(options.output); } else { print('cache hit, nothing to be done'); } @@ -47,10 +46,9 @@ export default async (cmd: Command, args: string[]) => { await run(args[0], args.slice(1)); - if (cache.shouldCacheOutput) { + if (cacheResult.archive) { print('caching build output'); - const archivePath = await cache.prepareOutput(key, options.output); - await createArchive(archivePath, options.output); + await cacheResult.archive(options.output); } } }; From 140cd7e4db030401bba660a1ffe934e0c3f1e529 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 27 Mar 2020 13:57:26 +0100 Subject: [PATCH 128/146] cli/commands/build-cache: remove unused archive function --- .../cli/src/commands/build-cache/archive.ts | 35 ------------------- 1 file changed, 35 deletions(-) diff --git a/packages/cli/src/commands/build-cache/archive.ts b/packages/cli/src/commands/build-cache/archive.ts index be74504f1f..b4a736240d 100644 --- a/packages/cli/src/commands/build-cache/archive.ts +++ b/packages/cli/src/commands/build-cache/archive.ts @@ -18,41 +18,6 @@ import fs from 'fs-extra'; import tar from 'tar'; import { dirname } from 'path'; -export async function readFileFromArchive( - archivePath: string, - filePath: string, -): Promise { - const reader = fs.createReadStream(archivePath); - const parser = new ((tar.Parse as unknown) as { new (): tar.ParseStream })(); - - const fileEntry = await new Promise((resolve, reject) => { - parser.on('entry', entry => { - if (entry.path === `./${filePath}`) { - resolve(entry); - reader.close(); - } else { - entry.resume(); - } - }); - parser.on('end', () => { - reject(new Error('cache archive did not contain build info')); - }); - parser.on('error', error => reject(error)); - reader.on('error', error => reject(error)); - - reader.pipe(parser); - }); - - const data = await new Promise((resolve, reject) => { - const chunks = new Array(); - fileEntry.on('data', chunk => chunks.push(chunk)); - fileEntry.on('end', () => resolve(Buffer.concat(chunks))); - fileEntry.on('error', error => reject(error)); - }); - - return data; -} - // packages all files in inputDir into an archive at archivePath, deleting any existing archive export async function createArchive( archivePath: string, From ec28ed8e2acd9f113984de0823d831527abcea91 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 27 Mar 2020 14:31:42 +0100 Subject: [PATCH 129/146] packages/app: rename build script to bundle and only run in CI when app changed --- .github/workflows/frontend.yml | 2 ++ package.json | 1 + packages/app/package.json | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index 8095106910..196cfd7541 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -43,5 +43,7 @@ jobs: - run: yarn lint - run: yarn build - run: yarn test + - name: yarn bundle, if app was changed + run: git diff --quiet origin/master HEAD -- packages/app || yarn bundle - name: verify storybook run: yarn workspace storybook build-storybook diff --git a/package.json b/package.json index eaf85f7567..f6c20e56fa 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ }, "scripts": { "start": "yarn build && yarn workspace example-app start", + "bundle": "yarn build && yarn workspace example-app bundle", "build": "lerna run build", "test": "cross-env CI=true lerna run test --since origin/master -- --coverage", "create-plugin": "backstage-cli create-plugin", diff --git a/packages/app/package.json b/packages/app/package.json index c332d2f0da..681593a369 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -26,7 +26,7 @@ }, "scripts": { "start": "backstage-cli app:serve", - "build": "backstage-cli app:build", + "bundle": "backstage-cli app:build", "test": "backstage-cli test", "test:e2e": "start-server-and-test start http://localhost:3000 cy:dev", "test:e2e:ci": "start-server-and-test start http://localhost:3000 cy:run", From e59ddb4d7c7efc35a84d1e77b578fa2d65c6bcec Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 27 Mar 2020 14:49:47 +0100 Subject: [PATCH 130/146] packages,plugins: use build-cache command --- packages/cli/package.json | 2 +- packages/cli/templates/default-plugin/package.json.hbs | 2 +- packages/core/package.json | 2 +- plugins/home-page/package.json | 2 +- plugins/welcome/package.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index ba039dcd5e..005fcf01ad 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -19,7 +19,7 @@ "main": "dist", "scripts": { "exec": "npx ts-node ./src", - "build": "tsc --outDir dist --noEmit false --module CommonJS", + "build": "backstage-cli build-cache -- tsc --outDir dist --noEmit false --module CommonJS", "lint": "backstage-cli lint", "test": "backstage-cli test", "start": "nodemon ." diff --git a/packages/cli/templates/default-plugin/package.json.hbs b/packages/cli/templates/default-plugin/package.json.hbs index b2bd6f2c82..c7220a8fd3 100644 --- a/packages/cli/templates/default-plugin/package.json.hbs +++ b/packages/cli/templates/default-plugin/package.json.hbs @@ -6,7 +6,7 @@ "license": "Apache-2.0", "private": true, "scripts": { - "build": "backstage-cli plugin:build", + "build": "backstage-cli build-cache -- backstage-cli plugin:build", "lint": "backstage-cli lint", "test": "backstage-cli test" }, diff --git a/packages/core/package.json b/packages/core/package.json index 8adb1307fe..1105d79c46 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -19,7 +19,7 @@ "main": "dist/cjs/index.js", "types": "dist/cjs/index.d.ts", "scripts": { - "build": "tsc --outDir dist/cjs --noEmit false --module CommonJS", + "build": "backstage-cli build-cache -- tsc --outDir dist/cjs --noEmit false --module CommonJS", "lint": "backstage-cli lint", "test": "backstage-cli test" }, diff --git a/plugins/home-page/package.json b/plugins/home-page/package.json index 33be457cba..91e3da417f 100644 --- a/plugins/home-page/package.json +++ b/plugins/home-page/package.json @@ -19,7 +19,7 @@ "react-dom": "^16.12.0" }, "scripts": { - "build": "backstage-cli plugin:build", + "build": "backstage-cli build-cache -- backstage-cli plugin:build", "lint": "backstage-cli lint", "test": "backstage-cli test" }, diff --git a/plugins/welcome/package.json b/plugins/welcome/package.json index b38977afab..e1672b4c40 100644 --- a/plugins/welcome/package.json +++ b/plugins/welcome/package.json @@ -6,7 +6,7 @@ "license": "Apache-2.0", "private": true, "scripts": { - "build": "backstage-cli plugin:build", + "build": "backstage-cli build-cache -- backstage-cli plugin:build", "lint": "backstage-cli lint", "test": "backstage-cli test" }, From e2014836b5b2c8773812f47fab54f2ab9dd3ceb1 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 27 Mar 2020 14:55:19 +0100 Subject: [PATCH 131/146] github/workflows: repurpose node_modules cache to cache .cache --- .github/workflows/frontend.yml | 6 +++--- .github/workflows/master.yml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index 196cfd7541..ad9744b12d 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -29,11 +29,11 @@ jobs: key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} restore-keys: | ${{ runner.os }}-yarn- - - name: cache node_modules + - name: cache node_modules/.cache uses: actions/cache@v1 with: - path: node_modules - key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} + path: node_modules/.cache + key: node_modules-cache - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index e82c6c3a4b..9a1791a594 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -27,11 +27,11 @@ jobs: key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} restore-keys: | ${{ runner.os }}-yarn- - - name: cache node_modules + - name: cache node_modules/.cache uses: actions/cache@v1 with: - path: node_modules - key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} + path: node_modules/.cache + key: node_modules-cache - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: From b87a760dd5fec4be396cbe4beac833886e317b9b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 27 Mar 2020 14:56:15 +0100 Subject: [PATCH 132/146] package: only lint changes since master --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f6c20e56fa..137c4de0bb 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "test": "cross-env CI=true lerna run test --since origin/master -- --coverage", "create-plugin": "backstage-cli create-plugin", "release": "if [ \"$(git symbolic-ref --short HEAD)\" = master ]; then echo \"don't try to release master\"; exit 1; else lerna version --no-push; fi", - "lint": "lerna run lint", + "lint": "cross-env CI=true lerna run lint --since origin/master --", "storybook": "yarn workspace storybook start" }, "workspaces": { From 3ca00be6071c5c97572f762e8732db973274e371 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 27 Mar 2020 15:07:21 +0100 Subject: [PATCH 133/146] cli/commands/build-cache: enable dirty check --- packages/cli/src/commands/build-cache/cache.ts | 9 +++++---- packages/cli/src/helpers/run.ts | 9 +++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/commands/build-cache/cache.ts b/packages/cli/src/commands/build-cache/cache.ts index d5ed39e148..da653a0331 100644 --- a/packages/cli/src/commands/build-cache/cache.ts +++ b/packages/cli/src/commands/build-cache/cache.ts @@ -16,7 +16,7 @@ import fs from 'fs-extra'; import { resolve as resolvePath, relative as relativePath } from 'path'; -import { runPlain } from '../../helpers/run'; +import { runPlain, runCheck } from '../../helpers/run'; import { Options } from './options'; import { extractArchive, createArchive } from './archive'; @@ -47,9 +47,10 @@ export class Cache { const location = resolvePath(options.cacheDir, repoPath); // Make sure we don't have any uncommitted changes to the input, in that case we consider the cache to be missing - try { - // await exec(`git diff --quiet HEAD -- ${options.inputs.join(' ')}`); - } catch (error) { + const noChanges = await runCheck( + `git diff --quiet HEAD -- ${options.inputs.join(' ')}`, + ); + if (!noChanges) { return new Cache(); } diff --git a/packages/cli/src/helpers/run.ts b/packages/cli/src/helpers/run.ts index 6a1376baf8..cbeb2c080c 100644 --- a/packages/cli/src/helpers/run.ts +++ b/packages/cli/src/helpers/run.ts @@ -62,6 +62,15 @@ export async function runPlain(cmd: string) { } } +export async function runCheck(cmd: string): Promise { + try { + await exec(cmd); + return true; + } catch (error) { + return false; + } +} + export async function waitForExit( child: ChildProcess & { exitCode?: number }, name?: string, From 2961e216ccbf032b241517820e18dcbd40d39549 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 27 Mar 2020 15:52:45 +0100 Subject: [PATCH 134/146] cli/commands/build-cache: allow cache dir to be set through env --- packages/cli/src/commands/build-cache/options.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/commands/build-cache/options.ts b/packages/cli/src/commands/build-cache/options.ts index 8df62afaa5..f70131ff0a 100644 --- a/packages/cli/src/commands/build-cache/options.ts +++ b/packages/cli/src/commands/build-cache/options.ts @@ -35,6 +35,8 @@ export async function parseOptions(cmd: Command): Promise { inputs.push(argTransformer('.')); } const output = argTransformer(cmd.output); - const cacheDir = argTransformer(cmd.cacheDir); + const cacheDir = argTransformer( + process.env.BACKSTAGE_CACHE_DIR || cmd.cacheDir, + ); return { inputs, output, cacheDir, repoRoot }; } From 9cb19a62aa5658645020d6500ec7cdecac35f8f6 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 27 Mar 2020 16:04:31 +0100 Subject: [PATCH 135/146] github/workflows: separate out build cache and use proper key + restore keys --- .github/workflows/frontend.yml | 9 ++++++--- .github/workflows/master.yml | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index ad9744b12d..cea02fec90 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -14,6 +14,7 @@ jobs: env: CI: true + BACKSTAGE_CACHE_DIR: /.backstage-build-cache steps: - uses: actions/checkout@v2 @@ -29,11 +30,13 @@ jobs: key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} restore-keys: | ${{ runner.os }}-yarn- - - name: cache node_modules/.cache + - name: cache build cache uses: actions/cache@v1 with: - path: node_modules/.cache - key: node_modules-cache + path: .backstage-build-cache + key: build-cache-${{ github.sha }} + restore-keys: | + build-cache- - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 9a1791a594..1b08426d29 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -14,6 +14,7 @@ jobs: env: CI: true + BACKSTAGE_CACHE_DIR: /.backstage-build-cache steps: - uses: actions/checkout@v2 @@ -27,11 +28,13 @@ jobs: key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} restore-keys: | ${{ runner.os }}-yarn- - - name: cache node_modules/.cache + - name: cache build cache uses: actions/cache@v1 with: - path: node_modules/.cache - key: node_modules-cache + path: .backstage-build-cache + key: build-cache-${{ github.sha }} + restore-keys: | + build-cache- - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: From 56a7969eac1fdbf66a5d85cfd9ecee996f2e414a Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 27 Mar 2020 16:36:37 +0100 Subject: [PATCH 136/146] github/workflows: add back node_modules cache --- .github/workflows/frontend.yml | 5 +++++ .github/workflows/master.yml | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index cea02fec90..05fcbd78d4 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -30,6 +30,11 @@ jobs: key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} restore-keys: | ${{ runner.os }}-yarn- + - name: cache node_modules + uses: actions/cache@v1 + with: + path: node_modules + key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} - name: cache build cache uses: actions/cache@v1 with: diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 1b08426d29..1b3b3e3900 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -28,6 +28,11 @@ jobs: key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} restore-keys: | ${{ runner.os }}-yarn- + - name: cache node_modules + uses: actions/cache@v1 + with: + path: node_modules + key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} - name: cache build cache uses: actions/cache@v1 with: From edd21b984eb8a57c0a30b636e09cd0255b5d3b00 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 27 Mar 2020 16:59:40 +0100 Subject: [PATCH 137/146] cli/commands/build-cache: make max entries configurable and set to 2 in CI --- .github/workflows/frontend.yml | 1 + .github/workflows/master.yml | 1 + packages/cli/src/commands/build-cache/cache.ts | 7 +++---- packages/cli/src/commands/build-cache/index.ts | 2 +- packages/cli/src/commands/build-cache/options.ts | 7 ++++++- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index 05fcbd78d4..478ff65744 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -15,6 +15,7 @@ jobs: env: CI: true BACKSTAGE_CACHE_DIR: /.backstage-build-cache + BACKSTAGE_CACHE_MAX_ENTRIES: 2 steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 1b3b3e3900..f36154aa48 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -15,6 +15,7 @@ jobs: env: CI: true BACKSTAGE_CACHE_DIR: /.backstage-build-cache + BACKSTAGE_CACHE_MAX_ENTRIES: 2 steps: - uses: actions/checkout@v2 diff --git a/packages/cli/src/commands/build-cache/cache.ts b/packages/cli/src/commands/build-cache/cache.ts index da653a0331..df91598e1e 100644 --- a/packages/cli/src/commands/build-cache/cache.ts +++ b/packages/cli/src/commands/build-cache/cache.ts @@ -20,13 +20,12 @@ import { runPlain, runCheck } from '../../helpers/run'; import { Options } from './options'; import { extractArchive, createArchive } from './archive'; -const MAX_ENTRIES = 10; const INFO_FILE = '.backstage-build-cache'; export type CacheQueryResult = { hit: boolean; copy?: (outputDir: string) => Promise; - archive?: (outputDir: string) => Promise; + archive?: (outputDir: string, maxEntries: number) => Promise; }; export type CacheKey = string[]; @@ -83,7 +82,7 @@ export class Cache { return { hit: false }; } - const archive = async (outputDir: string) => { + const archive = async (outputDir: string, maxEntries: number) => { await writeCacheInfo(outputDir, { key }); const timestamp = new Date().toISOString().replace(/-|:|\..*/g, ''); @@ -111,7 +110,7 @@ export class Cache { entries.unshift({ key, path: archiveName }); // Remove old cache entries - const removedEntries = entries.splice(MAX_ENTRIES); + const removedEntries = entries.splice(maxEntries); for (const entry of removedEntries) { try { await fs.remove(resolvePath(location, entry.path)); diff --git a/packages/cli/src/commands/build-cache/index.ts b/packages/cli/src/commands/build-cache/index.ts index 2aa2911116..8760d944af 100644 --- a/packages/cli/src/commands/build-cache/index.ts +++ b/packages/cli/src/commands/build-cache/index.ts @@ -48,7 +48,7 @@ export default async (cmd: Command, args: string[]) => { if (cacheResult.archive) { print('caching build output'); - await cacheResult.archive(options.output); + await cacheResult.archive(options.output, options.maxCacheEntries); } } }; diff --git a/packages/cli/src/commands/build-cache/options.ts b/packages/cli/src/commands/build-cache/options.ts index f70131ff0a..bad7d6c27e 100644 --- a/packages/cli/src/commands/build-cache/options.ts +++ b/packages/cli/src/commands/build-cache/options.ts @@ -18,10 +18,13 @@ import { resolve as resolvePath } from 'path'; import { Command } from 'commander'; import { runPlain } from '../../helpers/run'; +const DEFAULT_MAX_ENTRIES = 10; + export type Options = { inputs: string[]; output: string; cacheDir: string; + maxCacheEntries: number; repoRoot: string; }; @@ -38,5 +41,7 @@ export async function parseOptions(cmd: Command): Promise { const cacheDir = argTransformer( process.env.BACKSTAGE_CACHE_DIR || cmd.cacheDir, ); - return { inputs, output, cacheDir, repoRoot }; + const maxCacheEntries = + Number(process.env.BACKSTAGE_CACHE_MAX_ENTRIES) || DEFAULT_MAX_ENTRIES; + return { inputs, output, cacheDir, repoRoot, maxCacheEntries }; } From bfceff2c84e6840649fa1bd01f4ba3f0f4322d25 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 30 Mar 2020 18:06:32 +0200 Subject: [PATCH 138/146] cli/commands/build-cache: more docs --- .../cli/src/commands/build-cache/cache.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/cli/src/commands/build-cache/cache.ts b/packages/cli/src/commands/build-cache/cache.ts index df91598e1e..82159fc3ca 100644 --- a/packages/cli/src/commands/build-cache/cache.ts +++ b/packages/cli/src/commands/build-cache/cache.ts @@ -22,25 +22,41 @@ import { extractArchive, createArchive } from './archive'; const INFO_FILE = '.backstage-build-cache'; +// Result from a cache query export type CacheQueryResult = { + // True if there was a cache hit hit: boolean; + // If there is a cache hit and this method is defined, it needs to be called to restore the + // output contents before continuing. copy?: (outputDir: string) => Promise; + // If this method is defined, it should be called after a successful build to archive the output content. + // The content will be archived using the same key as was used in the cache. archive?: (outputDir: string, maxEntries: number) => Promise; }; +// Key that determines whether cached output can be reused export type CacheKey = string[]; type CacheEntry = { + // Key for the input of this cache entry key: CacheKey; + // Path to the archive of this cache entry path: string; }; +// Struct containing information about cache entries on the filesystem. +// Stored inside the INFO_FILE as JSON. type CacheInfo = { + // Optional key entry for the contents of the current directory. Used to key the + // output present in the outputs folder, where the info file resides inside the output folder. key?: CacheKey; + // Optional list of cache archives present in the same directory. Resides in the external + // cache location inside one info file for each package. entries?: CacheEntry[]; }; export class Cache { + // Read the current cache state form the filesystem. static async read(options: Options) { const repoPath = relativePath(options.repoRoot, process.cwd()); const location = resolvePath(options.cacheDir, repoPath); @@ -60,6 +76,7 @@ export class Cache { return new Cache(entries, location, localKey); } + // Generates a key based on the contents of the input paths static async readInputKey(inputPaths: string[]): Promise { const trees = []; for (const inputPath of inputPaths) { @@ -76,6 +93,7 @@ export class Cache { private readonly localKey?: CacheKey, ) {} + // Query for the presense of cached output for a given key query(key: CacheKey): CacheQueryResult { const { location } = this; if (!location) { @@ -142,6 +160,7 @@ export class Cache { } } +// Compares to cache keys, returning true if they are both defined and equal function compareKeys(a?: CacheKey, b?: CacheKey): boolean { if (!a || !b) { return false; @@ -149,6 +168,7 @@ function compareKeys(a?: CacheKey, b?: CacheKey): boolean { return a.join(',') === b.join(','); } +// Read and parse a cache info file in the given directory async function readCacheInfo( parentDir: string, ): Promise { @@ -163,6 +183,7 @@ async function readCacheInfo( return cacheInfo; } +// Write a cache info file to the given directory async function writeCacheInfo( parentDir: string, cacheInfo: CacheInfo, From c4b6afffaf095e5b9e34e87fa2656d2b5c0da9ad Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 30 Mar 2020 18:16:15 +0200 Subject: [PATCH 139/146] cli/commands/build-cache: refactor dirty check to make key undefined instead of cache --- .../cli/src/commands/build-cache/cache.ts | 42 ++++++++++--------- .../cli/src/commands/build-cache/index.ts | 26 +++++++----- 2 files changed, 38 insertions(+), 30 deletions(-) diff --git a/packages/cli/src/commands/build-cache/cache.ts b/packages/cli/src/commands/build-cache/cache.ts index 82159fc3ca..f52e081a3f 100644 --- a/packages/cli/src/commands/build-cache/cache.ts +++ b/packages/cli/src/commands/build-cache/cache.ts @@ -29,9 +29,9 @@ export type CacheQueryResult = { // If there is a cache hit and this method is defined, it needs to be called to restore the // output contents before continuing. copy?: (outputDir: string) => Promise; - // If this method is defined, it should be called after a successful build to archive the output content. - // The content will be archived using the same key as was used in the cache. - archive?: (outputDir: string, maxEntries: number) => Promise; + // Call after a successful build to archive the output content. + // The content will be archived using the same key as was used to query the cache. + archive: (outputDir: string, maxEntries: number) => Promise; }; // Key that determines whether cached output can be reused @@ -61,26 +61,31 @@ export class Cache { const repoPath = relativePath(options.repoRoot, process.cwd()); const location = resolvePath(options.cacheDir, repoPath); - // Make sure we don't have any uncommitted changes to the input, in that case we consider the cache to be missing - const noChanges = await runCheck( - `git diff --quiet HEAD -- ${options.inputs.join(' ')}`, - ); - if (!noChanges) { - return new Cache(); - } - const outputInfo = await readCacheInfo(options.output); const localKey = outputInfo?.key; const { entries = [] } = (await readCacheInfo(location)) ?? {}; - return new Cache(entries, location, localKey); + return new Cache(location, entries, localKey); } - // Generates a key based on the contents of the input paths - static async readInputKey(inputPaths: string[]): Promise { + // Generates a key based on the contents of the input paths. + // Returns undefined if it's not possible to generate a stable key. + static async readInputKey( + inputPaths: string[], + ): Promise { + const quotedInputPaths = inputPaths.map(input => `'${input}'`); + + // Make sure we don't have any uncommitted changes to the input, in that case we skip caching. + const noChanges = await runCheck( + `git diff --quiet HEAD -- ${quotedInputPaths.join(' ')}`, + ); + if (!noChanges) { + return undefined; + } + const trees = []; - for (const inputPath of inputPaths) { - const output = await runPlain(`git ls-tree HEAD '${inputPath}'`); + for (const quotedInputPath of quotedInputPaths) { + const output = await runPlain(`git ls-tree HEAD ${quotedInputPath}`); const [, , sha] = output.split(/\s+/, 3); trees.push(sha); } @@ -88,17 +93,14 @@ export class Cache { } constructor( + private readonly location: string, private readonly entries: CacheEntry[] = [], - private readonly location?: string, private readonly localKey?: CacheKey, ) {} // Query for the presense of cached output for a given key query(key: CacheKey): CacheQueryResult { const { location } = this; - if (!location) { - return { hit: false }; - } const archive = async (outputDir: string, maxEntries: number) => { await writeCacheInfo(outputDir, { key }); diff --git a/packages/cli/src/commands/build-cache/index.ts b/packages/cli/src/commands/build-cache/index.ts index 8760d944af..ab8984ecc3 100644 --- a/packages/cli/src/commands/build-cache/index.ts +++ b/packages/cli/src/commands/build-cache/index.ts @@ -30,8 +30,15 @@ function print(msg: string) { */ export default async (cmd: Command, args: string[]) => { const options = await parseOptions(cmd); - const cache = await Cache.read(options); + const key = await Cache.readInputKey(options.inputs); + if (!key) { + print('input directory is dirty, skipping cache'); + await run(args[0], args.slice(1)); + return; + } + + const cache = await Cache.read(options); const cacheResult = cache.query(key); if (cacheResult.hit) { @@ -41,14 +48,13 @@ export default async (cmd: Command, args: string[]) => { } else { print('cache hit, nothing to be done'); } - } else { - print('cache miss, need to build'); - - await run(args[0], args.slice(1)); - - if (cacheResult.archive) { - print('caching build output'); - await cacheResult.archive(options.output, options.maxCacheEntries); - } + return; } + + print('cache miss, need to build'); + + await run(args[0], args.slice(1)); + + print('caching build output'); + await cacheResult.archive(options.output, options.maxCacheEntries); }; From 7a4aebb8a29bdcbcf6fa204cf6a8f62c98d2f81d Mon Sep 17 00:00:00 2001 From: Sebastian Qvarfordt Date: Tue, 31 Mar 2020 13:04:51 +0200 Subject: [PATCH 140/146] initial rewrite to TS --- .../{ErrorBoundary.js => ErrorBoundary.tsx} | 37 ++++++++++++++----- 1 file changed, 27 insertions(+), 10 deletions(-) rename packages/core/src/layout/ErrorBoundary/{ErrorBoundary.js => ErrorBoundary.tsx} (67%) diff --git a/packages/core/src/layout/ErrorBoundary/ErrorBoundary.js b/packages/core/src/layout/ErrorBoundary/ErrorBoundary.tsx similarity index 67% rename from packages/core/src/layout/ErrorBoundary/ErrorBoundary.js rename to packages/core/src/layout/ErrorBoundary/ErrorBoundary.tsx index f9d5ec052a..6578cc94dd 100644 --- a/packages/core/src/layout/ErrorBoundary/ErrorBoundary.js +++ b/packages/core/src/layout/ErrorBoundary/ErrorBoundary.tsx @@ -14,17 +14,28 @@ * limitations under the License. */ -import React, { Component } from 'react'; +import React, { ComponentClass, Component, SFC } from 'react'; -export default class ErrorBoundary extends Component { + +type Props = { + slackChannel: string; +}; + +type State = { + error?: Error; + errorInfo?: string; + onError?: (error: Error, errorInfo: string) => null; +} + +const ErrorBoundary: ComponentClass = class ErrorBoundary extends Component { constructor(props) { super(props); this.state = { - error: null, - errorInfo: null, + error: undefined, + errorInfo: undefined, onError: props.onError, - }; + } } componentDidCatch(error, errorInfo) { @@ -33,8 +44,8 @@ export default class ErrorBoundary extends Component { this.setState({ error, errorInfo }); // Exposed for testing - if (ErrorBoundary.onError) { - ErrorBoundary.onError(error, errorInfo); + if (this.state.onError) { + this.state.onError(error, errorInfo); } } @@ -52,9 +63,15 @@ export default class ErrorBoundary extends Component { } } -// Importing Error would mean importing a lot of stuff -// will take it up in a separate PR -const Error = ({ slackChannel }) => { +export default ErrorBoundary; + +type EProps = { + error?: Error; + errorInfo?: string; + slackChannel: string; +} + +const Error: SFC = ({ slackChannel }) => { return (
Something went wrong here. Please contact {slackChannel} for help. From 924a9990798e51f48244535836be4559cc597f9a Mon Sep 17 00:00:00 2001 From: Sebastian Qvarfordt Date: Tue, 31 Mar 2020 13:07:21 +0200 Subject: [PATCH 141/146] prettier --- .../src/layout/ErrorBoundary/ErrorBoundary.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/core/src/layout/ErrorBoundary/ErrorBoundary.tsx b/packages/core/src/layout/ErrorBoundary/ErrorBoundary.tsx index 6578cc94dd..97738ac761 100644 --- a/packages/core/src/layout/ErrorBoundary/ErrorBoundary.tsx +++ b/packages/core/src/layout/ErrorBoundary/ErrorBoundary.tsx @@ -16,7 +16,6 @@ import React, { ComponentClass, Component, SFC } from 'react'; - type Props = { slackChannel: string; }; @@ -25,9 +24,12 @@ type State = { error?: Error; errorInfo?: string; onError?: (error: Error, errorInfo: string) => null; -} +}; -const ErrorBoundary: ComponentClass = class ErrorBoundary extends Component { +const ErrorBoundary: ComponentClass< + Props, + State +> = class ErrorBoundary extends Component { constructor(props) { super(props); @@ -35,7 +37,7 @@ const ErrorBoundary: ComponentClass = class ErrorBoundary extends error: undefined, errorInfo: undefined, onError: props.onError, - } + }; } componentDidCatch(error, errorInfo) { @@ -61,7 +63,7 @@ const ErrorBoundary: ComponentClass = class ErrorBoundary extends ); } -} +}; export default ErrorBoundary; @@ -69,7 +71,7 @@ type EProps = { error?: Error; errorInfo?: string; slackChannel: string; -} +}; const Error: SFC = ({ slackChannel }) => { return ( From 4e9151ba3ce0859e66229aa6a38b0c37175f2c91 Mon Sep 17 00:00:00 2001 From: Sebastian Qvarfordt Date: Tue, 31 Mar 2020 13:21:26 +0200 Subject: [PATCH 142/146] removed onError from state --- packages/core/src/layout/ErrorBoundary/ErrorBoundary.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/core/src/layout/ErrorBoundary/ErrorBoundary.tsx b/packages/core/src/layout/ErrorBoundary/ErrorBoundary.tsx index 97738ac761..34a903480e 100644 --- a/packages/core/src/layout/ErrorBoundary/ErrorBoundary.tsx +++ b/packages/core/src/layout/ErrorBoundary/ErrorBoundary.tsx @@ -18,12 +18,12 @@ import React, { ComponentClass, Component, SFC } from 'react'; type Props = { slackChannel: string; + onError?: (error: Error, errorInfo: string) => null; }; type State = { error?: Error; errorInfo?: string; - onError?: (error: Error, errorInfo: string) => null; }; const ErrorBoundary: ComponentClass< @@ -36,7 +36,6 @@ const ErrorBoundary: ComponentClass< this.state = { error: undefined, errorInfo: undefined, - onError: props.onError, }; } @@ -46,8 +45,8 @@ const ErrorBoundary: ComponentClass< this.setState({ error, errorInfo }); // Exposed for testing - if (this.state.onError) { - this.state.onError(error, errorInfo); + if (this.props.onError) { + this.props.onError(error, errorInfo); } } From 334abb29362c4d4922117130aa5900dfb75f6b00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Tue, 31 Mar 2020 15:00:17 +0200 Subject: [PATCH 143/146] Delete now.json --- now.json | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 now.json diff --git a/now.json b/now.json deleted file mode 100644 index 8ba35b1e6d..0000000000 --- a/now.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "version": 2, - "builds": [ - { "src": "./packages/app/build/**", "use": "@now/static" } - ], - "routes": [ - { "src": "/(.*)", "dest": "packages/app/build/$1" } - ] -} From c914a628824b42c05af35795b2e3eb7a8ab349c4 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 31 Mar 2020 15:31:22 +0200 Subject: [PATCH 144/146] github/workflows: also build app on changes to packages/core --- .github/workflows/frontend.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index 478ff65744..140969b332 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -53,6 +53,6 @@ jobs: - run: yarn build - run: yarn test - name: yarn bundle, if app was changed - run: git diff --quiet origin/master HEAD -- packages/app || yarn bundle + run: git diff --quiet origin/master HEAD -- packages/app packages/core || yarn bundle - name: verify storybook run: yarn workspace storybook build-storybook From 5a4228a28a385a6dd6a1867f1fcd9ffbefe4caa0 Mon Sep 17 00:00:00 2001 From: SrdjanCosicPrica <15933115+SrdjanCosicPrica@users.noreply.github.com> Date: Mon, 30 Mar 2020 18:58:37 +0200 Subject: [PATCH 145/146] Add husky and lint-staged to prettify staged files before commiting --- package.json | 12 ++++++++++++ yarn.lock | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index eaf85f7567..3cd109329b 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,9 @@ "devDependencies": { "cross-env": "^7.0.0", "eslint-plugin-notice": "^0.8.9", + "husky": "^4.2.3", "lerna": "^3.20.2", + "lint-staged": "^10.1.0", "prettier": "^1.19.1", "typescript": "^3.7.5", "zombie": "^6.1.4" @@ -31,5 +33,15 @@ "dependencies": { "@types/classnames": "^2.2.9", "moment": "^2.24.0" + }, + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, + "lint-staged": { + "*.{js,jsx,ts,tsx}": [ + "prettier --config ./prettier.config.js --list-different --write" + ] } } diff --git a/yarn.lock b/yarn.lock index dcde1de95a..d15eb99589 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6182,6 +6182,11 @@ compare-func@^1.3.1: array-ify "^1.0.0" dot-prop "^3.0.0" +compare-versions@^3.5.1: + version "3.6.0" + resolved "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62" + integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA== + component-emitter@^1.2.1: version "1.3.0" resolved "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" @@ -8618,7 +8623,7 @@ find-up@^2.0.0, find-up@^2.1.0: dependencies: locate-path "^2.0.0" -find-versions@^3.0.0: +find-versions@^3.0.0, find-versions@^3.2.0: version "3.2.0" resolved "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz#10297f98030a786829681690545ef659ed1d254e" integrity sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww== @@ -9798,6 +9803,22 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" +husky@^4.2.3: + version "4.2.3" + resolved "https://registry.npmjs.org/husky/-/husky-4.2.3.tgz#3b18d2ee5febe99e27f2983500202daffbc3151e" + integrity sha512-VxTsSTRwYveKXN4SaH1/FefRJYCtx+wx04sSVcOpD7N2zjoHxa+cEJ07Qg5NmV3HAK+IRKOyNVpi2YBIVccIfQ== + dependencies: + chalk "^3.0.0" + ci-info "^2.0.0" + compare-versions "^3.5.1" + cosmiconfig "^6.0.0" + find-versions "^3.2.0" + opencollective-postinstall "^2.0.2" + pkg-dir "^4.2.0" + please-upgrade-node "^3.2.0" + slash "^3.0.0" + which-pm-runs "^1.0.0" + hyphenate-style-name@^1.0.2, hyphenate-style-name@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz#097bb7fa0b8f1a9cf0bd5c734cf95899981a9b48" @@ -12156,6 +12177,25 @@ lint-staged@^10.0.4: string-argv "0.3.1" stringify-object "^3.3.0" +lint-staged@^10.1.0: + version "10.1.0" + resolved "https://registry.npmjs.org/lint-staged/-/lint-staged-10.1.0.tgz#18785bb005d5ed404f1c1db6563e082f7a7baac2" + integrity sha512-WzZ/T+O/aEaaT679sMgI4JqK5mnG69V5KQSouzVsShzZ8wGWte39HT3z61LsxjVNeCf8m/ChhvWJa2wTiQLy5A== + dependencies: + chalk "^3.0.0" + commander "^4.0.1" + cosmiconfig "^6.0.0" + debug "^4.1.1" + dedent "^0.7.0" + execa "^3.4.0" + listr "^0.14.3" + log-symbols "^3.0.0" + micromatch "^4.0.2" + normalize-path "^3.0.0" + please-upgrade-node "^3.2.0" + string-argv "0.3.1" + stringify-object "^3.3.0" + listr-silent-renderer@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e" @@ -13903,6 +13943,11 @@ open@^7.0.0, open@^7.0.2: is-docker "^2.0.0" is-wsl "^2.1.1" +opencollective-postinstall@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz#5657f1bede69b6e33a45939b061eb53d3c6c3a89" + integrity sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw== + opener@^1.5.1: version "1.5.1" resolved "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz#6d2f0e77f1a0af0032aca716c2c1fbb8e7e8abed" @@ -19231,6 +19276,11 @@ which-module@^2.0.0: resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= +which-pm-runs@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" + integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= + which@^1.2.14, which@^1.2.9, which@^1.3.0, which@^1.3.1: version "1.3.1" resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" From 124cdede70ae4fe25ffc7063f87c039358086b23 Mon Sep 17 00:00:00 2001 From: SrdjanCosicPrica <15933115+SrdjanCosicPrica@users.noreply.github.com> Date: Mon, 30 Mar 2020 18:59:32 +0200 Subject: [PATCH 146/146] Prettify project --- packages/app/src/react-app-env.d.ts | 2 - packages/core/.storybook/main.js | 4 +- .../core/src/api/apis/ApiTestRegistry.test.ts | 53 +++++++++--- packages/core/src/api/apis/ApiTestRegistry.ts | 34 ++++++-- packages/core/src/components/Status/index.ts | 10 ++- .../HeaderActionMenu/HeaderActionMenu.js | 26 +++++- .../HeaderActionMenu/HeaderActionMenu.test.js | 32 ++++++-- .../layout/HeaderLabel/HeaderLabel.test.js | 10 ++- .../layout/HeaderLabel/OwnerHeaderLabel.js | 81 +++++++++++++++++++ packages/core/src/testUtils/Keyboard.test.js | 22 +++-- packages/core/src/testUtils/mockBreakpoint.ts | 4 +- 11 files changed, 239 insertions(+), 39 deletions(-) create mode 100644 packages/core/src/layout/HeaderLabel/OwnerHeaderLabel.js diff --git a/packages/app/src/react-app-env.d.ts b/packages/app/src/react-app-env.d.ts index f4145f20db..f3b69cc361 100644 --- a/packages/app/src/react-app-env.d.ts +++ b/packages/app/src/react-app-env.d.ts @@ -13,5 +13,3 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - - diff --git a/packages/core/.storybook/main.js b/packages/core/.storybook/main.js index 06345558b4..160ecb3b0c 100644 --- a/packages/core/.storybook/main.js +++ b/packages/core/.storybook/main.js @@ -11,8 +11,8 @@ module.exports = { { loader: require.resolve('ts-loader'), options: { - transpileOnly: true - } + transpileOnly: true, + }, }, ], }); diff --git a/packages/core/src/api/apis/ApiTestRegistry.test.ts b/packages/core/src/api/apis/ApiTestRegistry.test.ts index 434de430f7..6974daa3a7 100644 --- a/packages/core/src/api/apis/ApiTestRegistry.test.ts +++ b/packages/core/src/api/apis/ApiTestRegistry.test.ts @@ -59,13 +59,24 @@ describe('ApiTestRegistry', () => { it('should register factories with dependencies', () => { // 100% coverage + happy typescript = hasOwnProperty + this atrocity - const cDeps = Object.create({ c: cRef }, { a: { enumerable: true, value: aRef } }); + const cDeps = Object.create( + { c: cRef }, + { a: { enumerable: true, value: aRef } }, + ); cDeps.b = bRef; const registry = new ApiTestRegistry(); registry.register({ implements: aRef, deps: {}, factory: () => 3 }); - registry.register({ implements: bRef, deps: { dep: aRef }, factory: ({ dep }) => `hello ${dep}` }); - registry.register({ implements: cRef, deps: cDeps, factory: ({ a, b }) => b.repeat(a) }); + registry.register({ + implements: bRef, + deps: { dep: aRef }, + factory: ({ dep }) => `hello ${dep}`, + }); + registry.register({ + implements: cRef, + deps: cDeps, + factory: ({ a, b }) => b.repeat(a), + }); expect(registry.get(aRef)).toBe(3); expect(registry.get(bRef)).toBe('hello 3'); expect(registry.get(cRef)).toBe('hello 3hello 3hello 3'); @@ -73,17 +84,39 @@ describe('ApiTestRegistry', () => { it('should not allow cyclic dependencies', () => { const registry = new ApiTestRegistry(); - registry.register({ implements: aRef, deps: { b: bRef }, factory: () => 1 }); - registry.register({ implements: bRef, deps: { c: cRef }, factory: () => 'b' }); - registry.register({ implements: cRef, deps: { a: aRef }, factory: () => 'c' }); - expect(() => registry.get(aRef)).toThrow('Circular dependency of api factory for apiRef{a}'); - expect(() => registry.get(bRef)).toThrow('Circular dependency of api factory for apiRef{b}'); - expect(() => registry.get(cRef)).toThrow('Circular dependency of api factory for apiRef{c}'); + registry.register({ + implements: aRef, + deps: { b: bRef }, + factory: () => 1, + }); + registry.register({ + implements: bRef, + deps: { c: cRef }, + factory: () => 'b', + }); + registry.register({ + implements: cRef, + deps: { a: aRef }, + factory: () => 'c', + }); + expect(() => registry.get(aRef)).toThrow( + 'Circular dependency of api factory for apiRef{a}', + ); + expect(() => registry.get(bRef)).toThrow( + 'Circular dependency of api factory for apiRef{b}', + ); + expect(() => registry.get(cRef)).toThrow( + 'Circular dependency of api factory for apiRef{c}', + ); }); it('should throw error if dependency is not available', () => { const registry = new ApiTestRegistry(); - registry.register({ implements: aRef, deps: { b: bRef }, factory: () => 1 }); + registry.register({ + implements: aRef, + deps: { b: bRef }, + factory: () => 1, + }); expect(() => registry.get(aRef)).toThrow( 'No API factory available for dependency apiRef{b} of dependent apiRef{a}', ); diff --git a/packages/core/src/api/apis/ApiTestRegistry.ts b/packages/core/src/api/apis/ApiTestRegistry.ts index c359b5878a..2af2058fbc 100644 --- a/packages/core/src/api/apis/ApiTestRegistry.ts +++ b/packages/core/src/api/apis/ApiTestRegistry.ts @@ -19,8 +19,14 @@ import { TypesToApiRefs, AnyApiRef, ApiHolder, ApiFactory } from './types'; export default class ApiTestRegistry implements ApiHolder { private readonly apis = new Map(); - private factories = new Map>(); - private savedFactories = new Map>(); + private factories = new Map< + AnyApiRef, + ApiFactory + >(); + private savedFactories = new Map< + AnyApiRef, + ApiFactory + >(); get(ref: ApiRef): T | undefined { return this.load(ref); @@ -28,7 +34,10 @@ export default class ApiTestRegistry implements ApiHolder { register(ref: ApiRef, factoryFunc: () => T): ApiTestRegistry; register(factory: ApiFactory): ApiTestRegistry; - register(factory: ApiRef | ApiFactory, factoryFunc?: () => T): ApiTestRegistry { + register( + factory: ApiRef | ApiFactory, + factoryFunc?: () => T, + ): ApiTestRegistry { if (factory instanceof ApiRef) { this.factories.set(factory, { implements: factory, @@ -63,16 +72,25 @@ export default class ApiTestRegistry implements ApiHolder { } if (loading.includes(factory.implements)) { - throw new Error(`Circular dependency of api factory for ${factory.implements}`); + throw new Error( + `Circular dependency of api factory for ${factory.implements}`, + ); } - const deps = this.loadDeps(ref, factory.deps, [...loading, factory.implements]); + const deps = this.loadDeps(ref, factory.deps, [ + ...loading, + factory.implements, + ]); const api = factory.factory(deps); this.apis.set(ref, api); return api as T; } - private loadDeps(dependent: ApiRef, apis: TypesToApiRefs, loading: AnyApiRef[]): T { + private loadDeps( + dependent: ApiRef, + apis: TypesToApiRefs, + loading: AnyApiRef[], + ): T { const impls = {} as T; for (const key in apis) { @@ -81,7 +99,9 @@ export default class ApiTestRegistry implements ApiHolder { const api = this.load(ref, loading); if (!api) { - throw new Error(`No API factory available for dependency ${ref} of dependent ${dependent}`); + throw new Error( + `No API factory available for dependency ${ref} of dependent ${dependent}`, + ); } impls[key] = api; } diff --git a/packages/core/src/components/Status/index.ts b/packages/core/src/components/Status/index.ts index 2f68d7d5bf..35d2ca1c48 100644 --- a/packages/core/src/components/Status/index.ts +++ b/packages/core/src/components/Status/index.ts @@ -14,4 +14,12 @@ * limitations under the License. */ -export { StatusError, StatusFailed, StatusNA, StatusOK, StatusPending, StatusRunning, StatusWarning } from './Status'; +export { + StatusError, + StatusFailed, + StatusNA, + StatusOK, + StatusPending, + StatusRunning, + StatusWarning, +} from './Status'; diff --git a/packages/core/src/layout/HeaderActionMenu/HeaderActionMenu.js b/packages/core/src/layout/HeaderActionMenu/HeaderActionMenu.js index 3ad10cfc30..409338caeb 100644 --- a/packages/core/src/layout/HeaderActionMenu/HeaderActionMenu.js +++ b/packages/core/src/layout/HeaderActionMenu/HeaderActionMenu.js @@ -15,10 +15,24 @@ */ import React, { Fragment } from 'react'; -import { IconButton, List, ListItem, ListItemIcon, ListItemText, Popover } from '@material-ui/core'; +import { + IconButton, + List, + ListItem, + ListItemIcon, + ListItemText, + Popover, +} from '@material-ui/core'; import { default as KebabMenuIcon } from './MenuVertical'; -const ActionItem = ({ label, secondaryLabel, icon, disabled = false, onClick, WrapperComponent = React.Fragment }) => { +const ActionItem = ({ + label, + secondaryLabel, + icon, + disabled = false, + onClick, + WrapperComponent = React.Fragment, +}) => { return ( { onClick={() => setOpen(true)} data-testid="header-action-menu" ref={anchorElRef} - style={{ color: 'white', height: 56, width: 56, marginRight: -4, padding: 0 }} + style={{ + color: 'white', + height: 56, + width: 56, + marginRight: -4, + padding: 0, + }} > diff --git a/packages/core/src/layout/HeaderActionMenu/HeaderActionMenu.test.js b/packages/core/src/layout/HeaderActionMenu/HeaderActionMenu.test.js index 97ea36c2ab..1b90cad6f8 100644 --- a/packages/core/src/layout/HeaderActionMenu/HeaderActionMenu.test.js +++ b/packages/core/src/layout/HeaderActionMenu/HeaderActionMenu.test.js @@ -27,13 +27,20 @@ describe('', () => { it('can open the menu and click menu items', () => { const onClickFunction = jest.fn(); const rendered = render( - wrapInThemedTestApp(), + wrapInThemedTestApp( + , + ), ); expect(rendered.queryByText('Some label')).not.toBeInTheDocument(); expect(onClickFunction).not.toHaveBeenCalled(); fireEvent.click(rendered.getByTestId('header-action-menu')); expect(onClickFunction).not.toHaveBeenCalled(); - expect(rendered.getByTestId('header-action-item')).not.toHaveAttribute('aria-disabled', 'true'); + expect(rendered.getByTestId('header-action-item')).not.toHaveAttribute( + 'aria-disabled', + 'true', + ); fireEvent.click(rendered.queryByText('Some label')); expect(onClickFunction).toHaveBeenCalled(); // We do not expect the dropdown to disappear after click @@ -42,11 +49,18 @@ describe('', () => { it('Disabled', async () => { const rendered = render( - wrapInThemedTestApp(), + wrapInThemedTestApp( + , + ), ); fireEvent.click(rendered.getByTestId('header-action-menu')); - expect(rendered.getByTestId('header-action-item')).toHaveAttribute('aria-disabled', 'true'); + expect(rendered.getByTestId('header-action-item')).toHaveAttribute( + 'aria-disabled', + 'true', + ); }); it('Test wrapper, and secondary label', () => { @@ -58,7 +72,9 @@ describe('', () => { { label: 'Some label', secondaryLabel: 'Secondary label', - WrapperComponent: ({ children }) => , + WrapperComponent: ({ children }) => ( + + ), }, ]} />, @@ -75,7 +91,11 @@ describe('', () => { }); it('should close when hitting escape', async () => { - const rendered = render(wrapInThemedTestApp()); + const rendered = render( + wrapInThemedTestApp( + , + ), + ); expect(rendered.container.getAttribute('aria-hidden')).toBeNull(); fireEvent.click(rendered.getByTestId('header-action-menu')); diff --git a/packages/core/src/layout/HeaderLabel/HeaderLabel.test.js b/packages/core/src/layout/HeaderLabel/HeaderLabel.test.js index 1c47d50794..2d9e97bb51 100644 --- a/packages/core/src/layout/HeaderLabel/HeaderLabel.test.js +++ b/packages/core/src/layout/HeaderLabel/HeaderLabel.test.js @@ -31,12 +31,18 @@ describe('', () => { }); it('should have value', () => { - const rendered = render(wrapInThemedTestApp()); + const rendered = render( + wrapInThemedTestApp(), + ); expect(rendered.getByText('Value')).toBeInTheDocument(); }); it('should have a link', () => { - const rendered = render(wrapInThemedTestApp()); + const rendered = render( + wrapInThemedTestApp( + , + ), + ); const anchor = rendered.container.querySelector('a'); expect(rendered.getByText('Value')).toBeInTheDocument(); expect(anchor.href).toBe('http://localhost/test'); diff --git a/packages/core/src/layout/HeaderLabel/OwnerHeaderLabel.js b/packages/core/src/layout/HeaderLabel/OwnerHeaderLabel.js new file mode 100644 index 0000000000..d13f0bd07a --- /dev/null +++ b/packages/core/src/layout/HeaderLabel/OwnerHeaderLabel.js @@ -0,0 +1,81 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { Tooltip, Link, withStyles } from '@material-ui/core'; + +import { StatusError } from '../../components/Status'; +import HeaderLabel from './HeaderLabel'; + +const style = theme => ({ + notVerified: { + color: theme.palette.status.error, + borderRadius: 4, + padding: '3px 6px', + fontSize: '8pt', + opacity: 0.8, + fontWeight: 'bold', + position: 'relative', + top: -4, + backgroundColor: 'pink', + float: 'right', + marginLeft: 14, + }, + label: { float: 'left' }, +}); + +class OwnerHeaderLabel extends Component { + static propTypes = { + owner: PropTypes.object.isRequired, + }; + + render() { + const { owner, classes } = this.props; + const isBadSquad = owner.type !== 'squad'; + + const notVerified = isBadSquad && ( + + + Squad not + verified! + + + ); + const label = ( + + + {owner.name} + + + ); + return ( + <> + + {notVerified} + + ); + } +} + +export default withStyles(style)(OwnerHeaderLabel); diff --git a/packages/core/src/testUtils/Keyboard.test.js b/packages/core/src/testUtils/Keyboard.test.js index 7615214744..31e0885dc1 100644 --- a/packages/core/src/testUtils/Keyboard.test.js +++ b/packages/core/src/testUtils/Keyboard.test.js @@ -33,8 +33,11 @@ describe('testUtils.Keyboard', () => { const rendered = render(
typed1.push(value)} /> - {/* eslint-disable-next-line jsx-a11y/no-autofocus */} - typed2.push(value)} autoFocus /> + typed2.push(value)} + /* eslint-disable-next-line jsx-a11y/no-autofocus */ + autoFocus + /> typed3.push(value)} /> , ); @@ -64,9 +67,18 @@ describe('testUtils.Keyboard', () => { const rendered = render(
- typed1.push(value)} /> - typed2.push(value)} /> - typed3.push(value)} /> + typed1.push(value)} + /> + typed2.push(value)} + /> + typed3.push(value)} + /> , ); diff --git a/packages/core/src/testUtils/mockBreakpoint.ts b/packages/core/src/testUtils/mockBreakpoint.ts index d92862cbc0..598ef654e8 100644 --- a/packages/core/src/testUtils/mockBreakpoint.ts +++ b/packages/core/src/testUtils/mockBreakpoint.ts @@ -29,7 +29,9 @@ const queryToBreakpoint = { function toBreakpoint(query: string) { const breakpoint = queryToBreakpoint[query]; if (!breakpoint) { - throw new Error(`received unknown media query in breakpoint mock: '${query}'`); + throw new Error( + `received unknown media query in breakpoint mock: '${query}'`, + ); } return breakpoint; }