diff --git a/package-lock.json b/package-lock.json index c17731bc..8790a100 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,9 +34,9 @@ } }, "@iconify-icons/codicon": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@iconify-icons/codicon/-/codicon-1.1.3.tgz", - "integrity": "sha512-Wp90AJVBd/T6FHxz7dGl+sNVAeoX1G3xXEbsj9oTBB92eXHswv6H+Z9Y8o5PDSe8pTR7hoG1uDx14SoC+8BtBw==", + "version": "1.1.8", + "resolved": "https://devdiv.pkgs.visualstudio.com/_packaging/CloudKernel/npm/registry/@iconify-icons/codicon/-/codicon-1.1.8.tgz", + "integrity": "sha1-chYidLCKt241LclUD7D2Xt61NsE=", "dev": true }, "@iconify/react": { @@ -51,6 +51,18 @@ "integrity": "sha512-cPqjjzuFWNK3BSKLm0abspP0sp/IGOli4p5I5fKFAzdS8fvjdOwDCfZqAaIiXd9lPkOWi3SUUfZof3hEb7J/uw==", "dev": true }, + "@reduxjs/toolkit": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.5.0.tgz", + "integrity": "sha512-E/FUraRx+8guw9Hlg/Ja8jI/hwCrmIKed8Annt9YsZw3BQp+F24t5I5b2OWR6pkEHY4hn1BgP08FrTZFRKsdaQ==", + "dev": true, + "requires": { + "immer": "^8.0.0", + "redux": "^4.0.0", + "redux-thunk": "^2.3.0", + "reselect": "^4.0.0" + } + }, "@restart/context": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@restart/context/-/context-2.1.4.tgz", @@ -99,6 +111,16 @@ "@types/node": "*" } }, + "@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "dev": true, + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "@types/invariant": { "version": "2.2.34", "resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.34.tgz", @@ -117,6 +139,12 @@ "integrity": "sha512-EP6O3Jkr7bXvZZSZYlsgt5DIjiGr0dXP1/jVEwVLTFgg0d+3lWVQkRavYVQszV7dYUwvg0B8R0MBDpcmXg7XIA==", "dev": true }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://devdiv.pkgs.visualstudio.com/_packaging/CloudKernel/npm/registry/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha1-PcoOPzOyAPx9ETnAzZbBJoyt/Z0=", + "dev": true + }, "@types/node": { "version": "8.10.63", "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.63.tgz", @@ -153,6 +181,18 @@ "@types/react": "*" } }, + "@types/react-redux": { + "version": "7.1.16", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.16.tgz", + "integrity": "sha512-f/FKzIrZwZk7YEO9E1yoxIuDNRiDducxkFlkw/GNMGEnK9n4K8wJzlJBghpSuOVDgEUHoDkDF7Gi9lHNQR4siw==", + "dev": true, + "requires": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + } + }, "@types/react-transition-group": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.0.tgz", @@ -404,7 +444,7 @@ "@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha1-7vAUoxRa5Hehy8AM0eVSM23Ot5A=", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", "dev": true }, "@xtuc/long": { @@ -464,7 +504,7 @@ "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -549,7 +589,7 @@ "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", "dev": true, "requires": { "sprintf-js": "~1.0.2" @@ -564,7 +604,7 @@ "arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=", "dev": true }, "arr-union": { @@ -632,7 +672,7 @@ }, "util": { "version": "0.10.3", - "resolved": "https://mseng.pkgs.visualstudio.com/DefaultCollection/_packaging/85c70207-5d33-4063-9f7f-3eb497df4d33/npm/registry/util/upstream/util-0.10.3.tgz", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "dev": true, "requires": { @@ -703,7 +743,7 @@ "autoprefixer": { "version": "8.6.5", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-8.6.5.tgz", - "integrity": "sha512-PLWJN3Xo/rycNkx+mp8iBDMTm3FeWe4VmYaZDSqL5QQB9sLsQkG5k8n+LNDFnhh9kdq2K+egL/icpctOmDHwig==", + "integrity": "sha1-ND89GT7VaLMgjgARehuW62kdTuk=", "dev": true, "requires": { "browserslist": "^3.2.8", @@ -744,7 +784,7 @@ "base": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=", "dev": true, "requires": { "cache-base": "^1.0.1", @@ -768,7 +808,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -777,7 +817,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -786,7 +826,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -814,7 +854,7 @@ "big.js": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "integrity": "sha1-pfwpi4G54Nyi5FiCR4S2XFK6WI4=", "dev": true }, "binary-extensions": { @@ -880,7 +920,7 @@ "braces": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "integrity": "sha1-WXn9PxTNUxVl5fot8av/8d+u5yk=", "dev": true, "requires": { "arr-flatten": "^1.1.0", @@ -940,7 +980,7 @@ "browserify-des": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha1-OvTx9Zg5QDVy8cZiBDdfen9wPpw=", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", "dev": true, "requires": { "cipher-base": "^1.0.1", @@ -1030,7 +1070,7 @@ "browserslist": { "version": "3.2.8", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz", - "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==", + "integrity": "sha1-sABTYdZHHw9ZUnl6dvyYXx+Xj8Y=", "dev": true, "requires": { "caniuse-lite": "^1.0.30000844", @@ -1059,7 +1099,7 @@ "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha1-MnE7wCj3XAL9txDXx7zsHyxgcO8=", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "dev": true }, "buffer-xor": { @@ -1129,7 +1169,7 @@ "cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=", "dev": true, "requires": { "collection-visit": "^1.0.0", @@ -1192,7 +1232,7 @@ "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "integrity": "sha1-GMSasWoDe26wFSzIPjRxM4IVtm4=", "dev": true, "requires": { "ansi-styles": "^3.2.1", @@ -1284,7 +1324,7 @@ "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "integrity": "sha1-+TNprouafOAv1B+q0MqDAzGQxGM=", "dev": true, "requires": { "arr-union": "^3.1.0", @@ -1549,7 +1589,7 @@ "cosmiconfig": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-4.0.0.tgz", - "integrity": "sha512-6e5vDdrXZD+t5v0L8CrurPeybg4Fmf+FCSYxXKYVAqLUtyCSbuyqE059d0kDthTNRzKVjL7QMgNpEUlsoYH3iQ==", + "integrity": "sha1-dgORVJWAu9LfHlYrwXexPCkJctw=", "dev": true, "requires": { "is-directory": "^0.3.1", @@ -1866,7 +1906,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", "dev": true, "requires": { "ms": "2.0.0" @@ -1896,7 +1936,7 @@ "define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "integrity": "sha1-1Flono1lS6d+AqgX+HENcCyxbp0=", "dev": true, "requires": { "is-descriptor": "^1.0.2", @@ -1906,7 +1946,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -1915,7 +1955,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -1924,7 +1964,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -2206,7 +2246,7 @@ "errno": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "integrity": "sha1-RoTXF3mtOa8Xfj8AeZb3xnyFJhg=", "dev": true, "requires": { "prr": "~1.0.1" @@ -2372,7 +2412,7 @@ "is-extendable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", "dev": true, "requires": { "is-plain-object": "^2.0.4" @@ -2383,7 +2423,7 @@ "extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "integrity": "sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=", "dev": true, "requires": { "array-unique": "^0.3.2", @@ -2417,7 +2457,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -2426,7 +2466,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -2435,7 +2475,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -2818,7 +2858,7 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=", "dev": true }, "gauge": { @@ -3124,6 +3164,15 @@ "minimalistic-crypto-utils": "^1.0.1" } }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dev": true, + "requires": { + "react-is": "^16.7.0" + } + }, "homedir-polyfill": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", @@ -3157,7 +3206,7 @@ "html-webpack-inline-source-plugin": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/html-webpack-inline-source-plugin/-/html-webpack-inline-source-plugin-0.0.10.tgz", - "integrity": "sha512-0ZNU57u7283vrXSF5a4VDnVOMWiSwypKIp1z/XfXWoVHLA1r3Xmyxx5+Lz+mnthz/UvxL1OAf41w5UIF68Jngw==", + "integrity": "sha1-ib1fdh5PFpAqp2pER261KDHJ9/A=", "dev": true, "requires": { "escape-string-regexp": "^1.0.5", @@ -3299,6 +3348,12 @@ "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", "dev": true }, + "immer": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/immer/-/immer-8.0.1.tgz", + "integrity": "sha512-aqXhGP7//Gui2+UrEtvxZxSquQVXTpZ7KDxfCcKAF3Vysvw0CViVaW9RZ1j1xlIYqaaaipBoqdqeibkc18PNvA==", + "dev": true + }, "import-cwd": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", @@ -3436,7 +3491,7 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=", "dev": true }, "is-callable": { @@ -3467,14 +3522,14 @@ }, "is-date-object": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "resolved": "https://mseng.pkgs.visualstudio.com/_packaging/85c70207-5d33-4063-9f7f-3eb497df4d33/npm/registry/is-date-object/upstream/is-date-object-1.0.1.tgz", "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", "dev": true }, "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", "dev": true, "requires": { "is-accessor-descriptor": "^0.1.6", @@ -3485,7 +3540,7 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", "dev": true } } @@ -3555,7 +3610,7 @@ "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=", "dev": true, "requires": { "isobject": "^3.0.1" @@ -3594,7 +3649,7 @@ "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "integrity": "sha1-0YUOuXkezRjmGCzhKjDzlmNLsZ0=", "dev": true }, "is-wsl": { @@ -3605,7 +3660,7 @@ }, "isarray": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "resolved": "https://mseng.pkgs.visualstudio.com/_packaging/85c70207-5d33-4063-9f7f-3eb497df4d33/npm/registry/isarray/upstream/isarray-0.0.1.tgz", "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", "dev": true }, @@ -3664,7 +3719,7 @@ "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "integrity": "sha1-u4Z8+zRQ5pEHwTHRxRS6s9yLyqk=", "dev": true }, "json-schema": { @@ -3825,7 +3880,7 @@ }, "map-cache": { "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "resolved": "https://mseng.pkgs.visualstudio.com/_packaging/85c70207-5d33-4063-9f7f-3eb497df4d33/npm/registry/map-cache/upstream/map-cache-0.2.2.tgz", "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", "dev": true }, @@ -3873,7 +3928,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -3889,7 +3944,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -3918,7 +3973,7 @@ "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "integrity": "sha1-cIWbyVyYQJUvNZoGij/En57PrCM=", "dev": true, "requires": { "arr-diff": "^4.0.0", @@ -3983,8 +4038,8 @@ }, "minimatch": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "resolved": "https://devdiv.pkgs.visualstudio.com/_packaging/CloudKernel/npm/registry/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -4098,13 +4153,13 @@ "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha1-ozeKdpbOfSI+iPybdkvX7xCJ42Y=", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, "no-case": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", - "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "integrity": "sha1-YLgTOWvjmz8SiKTB7V0efSi0ZKw=", "dev": true, "requires": { "lower-case": "^1.1.1" @@ -4307,7 +4362,7 @@ }, "normalize-range": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "resolved": "https://mseng.pkgs.visualstudio.com/DefaultCollection/_packaging/85c70207-5d33-4063-9f7f-3eb497df4d33/npm/registry/normalize-range/upstream/normalize-range-0.1.2.tgz", "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", "dev": true }, @@ -4334,7 +4389,7 @@ }, "num2fraction": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "resolved": "https://mseng.pkgs.visualstudio.com/DefaultCollection/_packaging/85c70207-5d33-4063-9f7f-3eb497df4d33/npm/registry/num2fraction/upstream/num2fraction-1.2.2.tgz", "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", "dev": true }, @@ -4608,7 +4663,7 @@ "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha1-1i27VnlAXXLEc37FhgDp3c8G0kw=", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, "path-type": { @@ -4704,7 +4759,7 @@ "postcss": { "version": "6.0.23", "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "integrity": "sha1-YcgswyisYOZ3ZF+XkFTrmLwOMyQ=", "dev": true, "requires": { "chalk": "^2.4.1", @@ -4715,7 +4770,7 @@ "postcss-load-config": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.0.0.tgz", - "integrity": "sha512-V5JBLzw406BB8UIfsAWSK2KSwIJ5yoEIVFb4gVkXci0QdKgA24jLmHZ/ghe/GgX0lJ0/D1uUK1ejhzEY94MChQ==", + "integrity": "sha1-8TEt2/WRLNdHF3CDxe96GdYu5IQ=", "dev": true, "requires": { "cosmiconfig": "^4.0.0", @@ -4725,7 +4780,7 @@ "postcss-loader": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-2.1.6.tgz", - "integrity": "sha512-hgiWSc13xVQAq25cVw80CH0l49ZKlAnU1hKPOdRrNj89bokRr/bZF2nT+hebPPF9c9xs8c3gw3Fr2nxtmXYnNg==", + "integrity": "sha1-HX3XsXxrojS5vtWvE+C+pApC10A=", "dev": true, "requires": { "loader-utils": "^1.1.0", @@ -4993,7 +5048,7 @@ "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "integrity": "sha1-o31zL0JxtKsa0HDTVQjoKQeI/6o=", "dev": true }, "promise-inflight": { @@ -5105,7 +5160,7 @@ "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "integrity": "sha1-yzroBuh0BERYTvFUzo7pjUA/PjY=", "dev": true }, "querystring": { @@ -5140,14 +5195,13 @@ } }, "react": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz", - "integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==", + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.1.tgz", + "integrity": "sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w==", "dev": true, "requires": { "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2" + "object-assign": "^4.1.1" } }, "react-bootstrap": { @@ -5177,15 +5231,14 @@ } }, "react-dom": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz", - "integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==", + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.1.tgz", + "integrity": "sha512-6eV150oJZ9U2t9svnsspTMrWNyHc6chX0KzDeAOXftRa8bNeOKTTfCJ7KorIwenkHd2xqVTBTCZd79yk/lx/Ug==", "dev": true, "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "scheduler": "^0.19.1" + "scheduler": "^0.20.1" } }, "react-is": { @@ -5216,6 +5269,19 @@ "warning": "^4.0.3" } }, + "react-redux": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.2.tgz", + "integrity": "sha512-8+CQ1EvIVFkYL/vu6Olo7JFLWop1qRUeb46sGtIMDCSpgwPQq8fPLpirIB0iTqFe9XYEFPHssdX8/UwN6pAkEA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.12.1", + "hoist-non-react-statics": "^3.3.2", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-is": "^16.13.1" + } + }, "react-transition-group": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", @@ -5251,7 +5317,7 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "https://mseng.pkgs.visualstudio.com/DefaultCollection/_packaging/85c70207-5d33-4063-9f7f-3eb497df4d33/npm/registry/readable-stream/upstream/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -5281,6 +5347,22 @@ "strip-indent": "^1.0.1" } }, + "redux": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz", + "integrity": "sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==", + "dev": true, + "requires": { + "loose-envify": "^1.4.0", + "symbol-observable": "^1.2.0" + } + }, + "redux-thunk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz", + "integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==", + "dev": true + }, "regenerator-runtime": { "version": "0.13.7", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", @@ -5290,7 +5372,7 @@ "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "integrity": "sha1-H07OJ+ALC2XgJHpoEOaoXYOldSw=", "dev": true, "requires": { "extend-shallow": "^3.0.2", @@ -5413,7 +5495,7 @@ "require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "integrity": "sha1-iaf92TgmEmcxjq/hT5wy5ZjDaQk=", "dev": true }, "require-main-filename": { @@ -5422,6 +5504,12 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, + "reselect": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.0.0.tgz", + "integrity": "sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA==", + "dev": true + }, "resolve": { "version": "1.14.2", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.14.2.tgz", @@ -5478,7 +5566,7 @@ "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "integrity": "sha1-uKSCXVvbH8P29Twrwz+BOIaBx7w=", "dev": true }, "rimraf": { @@ -5512,7 +5600,7 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=", "dev": true }, "safe-regex": { @@ -5527,7 +5615,7 @@ "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=", "dev": true }, "sass-graph": { @@ -5570,9 +5658,9 @@ } }, "scheduler": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", - "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.1.tgz", + "integrity": "sha512-LKTe+2xNJBNxu/QhHvDR14wUXHRQbVY5ZOYpOGWRzhydZUqrLb2JBvLPY7cAqFmqrWuDED0Mjk7013SZiOz6Bw==", "dev": true, "requires": { "loose-envify": "^1.1.0", @@ -5713,7 +5801,7 @@ "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "integrity": "sha1-ZJIufFZbDhQgS6GqfWlkJ40lGC0=", "dev": true, "requires": { "base": "^0.11.1", @@ -5755,7 +5843,7 @@ "snapdragon-node": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=", "dev": true, "requires": { "define-property": "^1.0.0", @@ -5775,7 +5863,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -5784,7 +5872,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -5793,7 +5881,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -5806,7 +5894,7 @@ "snapdragon-util": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=", "dev": true, "requires": { "kind-of": "^3.2.0" @@ -5838,7 +5926,7 @@ "source-map-resolve": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "integrity": "sha1-cuLMNAlVQ+Q7LGKyxMENSpBU8lk=", "dev": true, "requires": { "atob": "^2.1.1", @@ -5899,7 +5987,7 @@ "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=", "dev": true, "requires": { "extend-shallow": "^3.0.0" @@ -5907,7 +5995,7 @@ }, "sprintf-js": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "resolved": "https://mseng.pkgs.visualstudio.com/DefaultCollection/_packaging/85c70207-5d33-4063-9f7f-3eb497df4d33/npm/registry/sprintf-js/upstream/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, @@ -5966,7 +6054,7 @@ "stdout-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", - "integrity": "sha1-WsF0zdXNcmEEqgwLK9g4FdjVNd4=", + "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", "dev": true, "requires": { "readable-stream": "^2.0.1" @@ -5996,7 +6084,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -6055,7 +6143,7 @@ "stream-each": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", - "integrity": "sha1-6+J6DDibBPvMIzZClS4Qcxr6m64=", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", "dev": true, "requires": { "end-of-stream": "^1.1.0", @@ -6065,7 +6153,7 @@ "stream-http": { "version": "2.8.3", "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", - "integrity": "sha1-stJCRpKIpaJ+xP6JM6z2I95lFPw=", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", "dev": true, "requires": { "builtin-status-codes": "^3.0.0", @@ -6160,7 +6248,7 @@ "style-loader": { "version": "0.21.0", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.21.0.tgz", - "integrity": "sha512-T+UNsAcl3Yg+BsPKs1vd22Fr8sVT+CJMtzqc6LEw9bbJZb43lm9GoeIfUcDEefBSWC0BhYbcdupV1GtI4DGzxg==", + "integrity": "sha1-aMUuXrKvycqStidL4nfuWa6jqFI=", "dev": true, "requires": { "loader-utils": "^1.1.0", @@ -6176,6 +6264,12 @@ "has-flag": "^3.0.0" } }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", + "dev": true + }, "tapable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.0.tgz", @@ -6331,7 +6425,7 @@ "to-regex": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "integrity": "sha1-E8/dmzNlUvMLUfM6iuG0Knp1mc4=", "dev": true, "requires": { "define-property": "^2.0.2", @@ -6375,7 +6469,7 @@ "true-case-path": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", - "integrity": "sha1-+BO1qMhrQNpZYGcisUTjIleZ9H0=", + "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", "dev": true, "requires": { "glob": "^7.1.2" @@ -6424,7 +6518,7 @@ "tsutils": { "version": "2.29.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha1-MrSIUBRnrL7dS4VJhnOggSrKC5k=", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", "dev": true, "requires": { "tslib": "^1.8.1" @@ -6600,7 +6694,7 @@ "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "integrity": "sha1-lMVA4f93KVbiKZUHwBCupsiDjrA=", "dev": true, "requires": { "punycode": "^2.1.0" @@ -6755,7 +6849,7 @@ "util.promisify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "integrity": "sha1-RA9xZaRZyaFtwUXrjnLzVocJcDA=", "dev": true, "requires": { "define-properties": "^1.1.2", @@ -6783,7 +6877,7 @@ "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha1-/JH2uce6FchX9MssXe/uw51PQQo=", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, "requires": { "spdx-correct": "^3.0.0", diff --git a/package.json b/package.json index a946db6c..5691d3a1 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,9 @@ "onCommand:java.extGuide", "onWebviewPanel:java.extGuide", "onCommand:java.welcome", - "onWebviewPanel:java.welcome" + "onWebviewPanel:java.welcome", + "onCommand:java.classpathConfiguration", + "onWebviewPanel:java.classpathConfiguration" ], "contributes": { "commands": [ @@ -54,6 +56,11 @@ "category": "Java", "title": "Welcome" }, + { + "command": "java.classpathConfiguration", + "category": "Java", + "title": "Configure Classpath" + }, { "command": "java.overview", "title": "Java: Overview" @@ -126,15 +133,18 @@ "build": "webpack --config webpack.config.js --mode=\"production\"" }, "devDependencies": { - "@iconify-icons/codicon": "^1.1.3", + "@iconify-icons/codicon": "^1.1.8", "@iconify/react": "^1.1.3", + "@reduxjs/toolkit": "^1.5.0", "@types/bytes": "^3.1.0", "@types/expand-tilde": "^2.0.0", "@types/lodash": "^4.14.161", + "@types/minimatch": "^3.0.3", "@types/node": "^8.10.63", "@types/path-exists": "^3.0.0", "@types/react": "^17.0.0", "@types/react-dom": "^16.9.8", + "@types/react-redux": "^7.1.16", "@types/request": "^2.48.5", "@types/request-promise-native": "^1.0.17", "@types/semver": "^5.5.0", @@ -152,13 +162,15 @@ "html-webpack-plugin": "^3.2.0", "jquery": "^3.5.1", "lodash": "^4.17.20", + "minimatch": "^3.0.4", "node-sass": "^4.14.1", "path-exists": "^3.0.0", "popper.js": "^1.16.1", "postcss-loader": "^2.1.5", - "react": "^16.13.1", + "react": "^17.0.1", "react-bootstrap": "^1.4.3", - "react-dom": "^16.13.1", + "react-dom": "^17.0.1", + "react-redux": "^7.2.2", "request": "^2.88.2", "request-promise-native": "^1.0.9", "sass-loader": "^7.3.1", @@ -166,8 +178,8 @@ "ts-loader": "^4.3.0", "tslint": "^5.20.1", "typescript": "^3.7.5", - "vscode-tas-client": "^0.1.17", "url-loader": "^4.1.1", + "vscode-tas-client": "^0.1.17", "webpack": "^4.44.1", "webpack-cli": "^3.3.12" }, diff --git a/src/classpath/assets/App.tsx b/src/classpath/assets/App.tsx new file mode 100644 index 00000000..1baf059e --- /dev/null +++ b/src/classpath/assets/App.tsx @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import React from "react"; +import ClasspathConfigurationView from "./features/classpathConfiguration/ClasspathConfigurationView"; + +export class App extends React.Component { + + render() { + return ( + + ); + } +} diff --git a/src/classpath/assets/app/store.ts b/src/classpath/assets/app/store.ts new file mode 100644 index 00000000..1b8241a0 --- /dev/null +++ b/src/classpath/assets/app/store.ts @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { configureStore } from "@reduxjs/toolkit"; +import classpathConfigurationViewReducer from "../features/classpathConfiguration/classpathConfigurationViewSlice"; + +export default configureStore({ + reducer: { + classpathConfig: classpathConfigurationViewReducer + }, +}); diff --git a/src/classpath/assets/features/classpathConfiguration/ClasspathConfigurationView.tsx b/src/classpath/assets/features/classpathConfiguration/ClasspathConfigurationView.tsx new file mode 100644 index 00000000..289ecdc7 --- /dev/null +++ b/src/classpath/assets/features/classpathConfiguration/ClasspathConfigurationView.tsx @@ -0,0 +1,100 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import React, { useEffect } from "react"; +import { Col, Container, Row, Spinner } from "react-bootstrap"; +import { useSelector, useDispatch } from "react-redux"; +import { Dispatch } from "@reduxjs/toolkit"; +import Output from "./components/Output"; +import ProjectSelector from "./components/ProjectSelector"; +import Sources from "./components/Sources"; +import ReferencedLibraries from "./components/ReferencedLibraries"; +import Header from "./components/Header"; +import Exception from "./components/Exception"; +import { ClasspathViewException, ProjectInfo } from "../../../types"; +import { catchException, listProjects, loadClasspath } from "./classpathConfigurationViewSlice"; +import JdkRuntime from "./components/JdkRuntime"; + +const ClasspathConfigurationView = (): JSX.Element => { + const projects: ProjectInfo[] = useSelector((state: any) => state.classpathConfig.projects); + const exception: ClasspathViewException | undefined = useSelector((state: any) => state.classpathConfig.exception); + let content: JSX.Element; + + if (exception) { + content = ; + } else if (projects.length === 0) { + content = Loading...; + } else { + content = ( +
+ + + + + + + + + + + + + + + + + + + + + +
+ ); + } + + const dispatch: Dispatch = useDispatch(); + + const onInitialize = (event: OnInitializeEvent) => { + const {data} = event; + if (data.command === "onDidListProjects") { + dispatch(listProjects(data.projectInfo)); + } else if (data.command === "onDidLoadProjectClasspath") { + dispatch(loadClasspath(data)); + } else if (data.command === "onException") { + dispatch(catchException(data.exception)); + } + }; + + useEffect(() => { + window.addEventListener("message", onInitialize); + return () => window.removeEventListener("message", onInitialize); + }, []); + + return ( + + + +
+ + + {content} + + ); +}; + +interface OnInitializeEvent { + data: { + command: string; + projectInfo?: { + name: string; + rootPath: string; + projectType: string; + }[]; + sources?: string[]; + output?: string; + referencedLibraries?: string[]; + exception?: ClasspathViewException; + }; +} + +export default ClasspathConfigurationView; diff --git a/src/classpath/assets/features/classpathConfiguration/classpathConfigurationViewSlice.ts b/src/classpath/assets/features/classpathConfiguration/classpathConfigurationViewSlice.ts new file mode 100644 index 00000000..eb2a77a4 --- /dev/null +++ b/src/classpath/assets/features/classpathConfiguration/classpathConfigurationViewSlice.ts @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { createSlice } from "@reduxjs/toolkit"; +import _ from "lodash"; + +export const classpathConfigurationViewSlice = createSlice({ + name: "classpathConfig", + initialState: { + activeProjectIndex: 0, + projects: [], + projectType: undefined, + sources: [] as string[], + output: "", + referencedLibraries: [] as string[], + exception: undefined, + }, + reducers: { + listProjects: (state, action) => { + state.projects = action.payload; + state.activeProjectIndex = 0; + }, + activeProjectChange: (state, action) => { + state.activeProjectIndex = action.payload; + }, + loadClasspath: (state, action) => { + state.projectType = action.payload.projectType; + state.output = action.payload.output; + // Only update the array when they have different elements. + if (isDifferentStringArray(state.sources, action.payload.sources)) { + state.sources = action.payload.sources; + } + if (isDifferentStringArray(state.referencedLibraries, action.payload.referencedLibraries)) { + state.referencedLibraries = action.payload.referencedLibraries; + } + }, + updateSource: (state, action) => { + state.sources = action.payload; + }, + setOutputPath: (state, action) => { + state.output = action.payload; + }, + removeReferencedLibrary: (state, action) => { + const removedIndex: number = action.payload as number; + if (removedIndex > -1 && removedIndex < state.referencedLibraries.length) { + state.referencedLibraries.splice(removedIndex, 1); + } + }, + addReferencedLibraries: (state, action) => { + state.referencedLibraries.push(...action.payload); + state.referencedLibraries = _.uniq(state.referencedLibraries); + }, + catchException: (state, action) => { + state.exception = action.payload; + } + }, +}); + +function isDifferentStringArray(a1: string[], a2: string[]): boolean { + return !_.isEmpty(_.xor(a1, a2)); +} + +export const { + listProjects, + activeProjectChange, + loadClasspath, + updateSource, + setOutputPath, + removeReferencedLibrary, + addReferencedLibraries, + catchException, +} = classpathConfigurationViewSlice.actions; + +export default classpathConfigurationViewSlice.reducer; diff --git a/src/classpath/assets/features/classpathConfiguration/components/Exception.tsx b/src/classpath/assets/features/classpathConfiguration/components/Exception.tsx new file mode 100644 index 00000000..01a243d9 --- /dev/null +++ b/src/classpath/assets/features/classpathConfiguration/components/Exception.tsx @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import React from "react"; +import { useSelector } from "react-redux"; +import { encodeCommandUriWithTelemetry, supportedByNavigator } from "../../../../../utils/webview"; +import { ClasspathViewException } from "../../../../types"; +import { WEBVIEW_ID } from "../../../utils"; + +const Exception = (): JSX.Element | null => { + const exception: ClasspathViewException | undefined = useSelector((state: any) => state.classpathConfig.exception); + + let content: JSX.Element; + switch (exception) { + case ClasspathViewException.NoJavaProjects: + let command: string = "workbench.action.files.openFolder"; + if (supportedByNavigator("mac")) { + command = "workbench.action.files.openFileFolder"; + } + content = ( +
+ There is no Java projects opened in the current workspace. Please open a Java project. +
+ ); + break; + case ClasspathViewException.JavaExtensionNotInstalled: + content = ( +
+ The required extension Language Support for Java(TM) by Red Hat is not installed or the version is too old. Please install it in Visual Studio Code and refresh the page after the installation. +
+ ); + break; + default: + return null; + } + + return content; +}; + +export default Exception; diff --git a/src/classpath/assets/features/classpathConfiguration/components/Header.tsx b/src/classpath/assets/features/classpathConfiguration/components/Header.tsx new file mode 100644 index 00000000..15bd28a7 --- /dev/null +++ b/src/classpath/assets/features/classpathConfiguration/components/Header.tsx @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import React from "react"; + +const Header = (): JSX.Element => { + return ( +

Configure Classpath

+ ); +}; + +export default Header; diff --git a/src/classpath/assets/features/classpathConfiguration/components/JdkRuntime.tsx b/src/classpath/assets/features/classpathConfiguration/components/JdkRuntime.tsx new file mode 100644 index 00000000..d59d2a2c --- /dev/null +++ b/src/classpath/assets/features/classpathConfiguration/components/JdkRuntime.tsx @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import React from "react"; +import { encodeCommandUriWithTelemetry } from "../../../../../utils/webview"; +import { WEBVIEW_ID } from "../../../utils"; + +const JdkRuntime = (): JSX.Element => { + return ( +
+

JDK Runtime

+ Map Java execution environment to local JDKs. Edit in Configure Java Runtime. +
+ ); +}; + +export default JdkRuntime; diff --git a/src/classpath/assets/features/classpathConfiguration/components/Output.tsx b/src/classpath/assets/features/classpathConfiguration/components/Output.tsx new file mode 100644 index 00000000..766b54af --- /dev/null +++ b/src/classpath/assets/features/classpathConfiguration/components/Output.tsx @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { Dispatch } from "@reduxjs/toolkit"; +import React, { useEffect } from "react"; +import { useSelector, useDispatch } from "react-redux"; +import { ProjectType } from "../../../../types"; +import { onWillSelectOutputPath } from "../../../utils"; +import { setOutputPath } from "../classpathConfigurationViewSlice"; + +const Output = (): JSX.Element => { + const output: string = useSelector((state: any) => state.classpathConfig.output); + const projectType: ProjectType = useSelector((state: any) => state.classpathConfig.projectType); + const dispatch: Dispatch = useDispatch(); + const handleClick = () => { + onWillSelectOutputPath(); + }; + + const onDidSelectOutputPath = (event: OnDidSelectOutputPathEvent) => { + const {data} = event; + if (data.command === "onDidSelectOutputPath") { + dispatch(setOutputPath(data.output)); + } + }; + + useEffect(() => { + window.addEventListener("message", onDidSelectOutputPath); + return () => window.removeEventListener("message", onDidSelectOutputPath); + }, []); + + return ( +
+
+

Output

+ {projectType !== ProjectType.UnmanagedFolder && + (Read-only) + } +
+ Specify compile output path location. +
{output}
+ {projectType === ProjectType.UnmanagedFolder && + handleClick()}> + Browse + + } +
+ ); +}; + +interface OnDidSelectOutputPathEvent { + data: { + command: string + output: string + }; +} + +export default Output; diff --git a/src/classpath/assets/features/classpathConfiguration/components/ProjectSelector.tsx b/src/classpath/assets/features/classpathConfiguration/components/ProjectSelector.tsx new file mode 100644 index 00000000..a6f55a3a --- /dev/null +++ b/src/classpath/assets/features/classpathConfiguration/components/ProjectSelector.tsx @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import React, { useEffect } from "react"; +import { Dropdown } from "react-bootstrap"; +import { useSelector, useDispatch } from "react-redux"; +import { ProjectInfo, ProjectType } from "../../../../types"; +import { Col, Row } from "react-bootstrap"; +import { Dispatch } from "@reduxjs/toolkit"; +import { activeProjectChange } from "../classpathConfigurationViewSlice"; +import { onClickGotoProjectConfiguration, onWillLoadProjectClasspath } from "../../../utils"; + +const ProjectSelector = (): JSX.Element | null => { + const activeProjectIndex: number = useSelector((state: any) => state.classpathConfig.activeProjectIndex); + const projects: ProjectInfo[] = useSelector((state: any) => state.classpathConfig.projects); + const projectType: ProjectType = useSelector((state: any) => state.classpathConfig.projectType); + let buildFile: string = ""; + if (projectType === ProjectType.Maven) { + buildFile = "pom.xml"; + } else if (projectType === ProjectType.Gradle) { + buildFile = "build.gradle"; + } + + const dispatch: Dispatch = useDispatch(); + + const handleActiveProjectChange = (index: number) => { + dispatch(activeProjectChange(index)); + }; + + const handleOpenBuildFile = () => { + onClickGotoProjectConfiguration(projects[activeProjectIndex].rootPath, projectType); + }; + + useEffect(() => { + onWillLoadProjectClasspath(projects[activeProjectIndex].rootPath); + }, [activeProjectIndex, projects]); + + const projectSelections = projects.map((project, index) => { + return ( + handleActiveProjectChange(index)}> + {project.name} + + ); + }); + + return ( + + + Select the project. + + + {projects[activeProjectIndex].name} + + + + {projectSelections} + + + {(projectType === ProjectType.Gradle || projectType === ProjectType.Maven) && +
+ + Below settings are only applicable for non-build tool projects. For the {projectType} project, please edit them in the handleOpenBuildFile()}>{buildFile} file. + +
+ } + +
+ ); +}; + +export default ProjectSelector; diff --git a/src/classpath/assets/features/classpathConfiguration/components/ReferencedLibraries.tsx b/src/classpath/assets/features/classpathConfiguration/components/ReferencedLibraries.tsx new file mode 100644 index 00000000..d05b0604 --- /dev/null +++ b/src/classpath/assets/features/classpathConfiguration/components/ReferencedLibraries.tsx @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { Dispatch } from "@reduxjs/toolkit"; +import React, { useEffect } from "react"; +import { useSelector, useDispatch } from "react-redux"; +import { ListGroup } from "react-bootstrap"; +import { Icon } from "@iconify/react"; +import closeIcon from "@iconify-icons/codicon/chrome-close"; +import { removeReferencedLibrary, addReferencedLibraries } from "../classpathConfigurationViewSlice"; +import { onWillAddReferencedLibraries, onWillRemoveReferencedLibraries } from "../../../utils"; +import { ProjectType } from "../../../../types"; + +const ReferencedLibraries = (): JSX.Element => { + const referencedLibraries: string[] = useSelector((state: any) => state.classpathConfig.referencedLibraries); + const projectType: ProjectType = useSelector((state: any) => state.classpathConfig.projectType); + const dispatch: Dispatch = useDispatch(); + + const handleRemove = (index: number) => { + onWillRemoveReferencedLibraries(referencedLibraries[index]); + dispatch(removeReferencedLibrary(index)); + }; + + const handleAdd = () => { + onWillAddReferencedLibraries(); + }; + + const onDidAddReferencedLibraries = (event: OnDidAddReferencedLibrariesEvent) => { + const {data} = event; + if (data.command === "onDidAddReferencedLibraries") { + dispatch(addReferencedLibraries(data.jars)); + } + }; + + useEffect(() => { + window.addEventListener("message", onDidAddReferencedLibraries); + return () => window.removeEventListener("message", onDidAddReferencedLibraries); + }, []); + + let referencedLibrariesSections: JSX.Element | JSX.Element[]; + if (referencedLibraries.length === 0) { + referencedLibrariesSections = ( + + No referenced libraries are configured. + + ); + } else { + referencedLibrariesSections = referencedLibraries.map((library, index) => ( + + {library} + {projectType === ProjectType.UnmanagedFolder && + + handleRemove(index)}> + + + + } + + )); + } + + return ( +
+
+

Referenced Libraries

+ {projectType !== ProjectType.UnmanagedFolder && + (Read-only) + } +
+ Specify referenced libraries of the project. + + + Path + + {referencedLibrariesSections} + + {projectType === ProjectType.UnmanagedFolder && + handleAdd()}>Add + } +
+ ); +}; + +interface OnDidAddReferencedLibrariesEvent { + data: { + command: string; + jars: string[]; + }; +} + +export default ReferencedLibraries; diff --git a/src/classpath/assets/features/classpathConfiguration/components/Sources.tsx b/src/classpath/assets/features/classpathConfiguration/components/Sources.tsx new file mode 100644 index 00000000..527a0e9e --- /dev/null +++ b/src/classpath/assets/features/classpathConfiguration/components/Sources.tsx @@ -0,0 +1,98 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { Icon } from "@iconify/react"; +import closeIcon from "@iconify-icons/codicon/chrome-close"; +import React, { useEffect } from "react"; +import { useSelector, useDispatch } from "react-redux"; +import { ListGroup } from "react-bootstrap"; +import { Dispatch } from "@reduxjs/toolkit"; +import { updateSource } from "../classpathConfigurationViewSlice"; +import { onWillAddSourcePath, onWillRemoveSourcePath } from "../../../utils"; +import { ProjectType } from "../../../../types"; + +const Sources = (): JSX.Element => { + const sources: string[] = useSelector((state: any) => state.classpathConfig.sources); + const projectType: ProjectType = useSelector((state: any) => state.classpathConfig.projectType); + const dispatch: Dispatch = useDispatch(); + + const handleRemove = (source: string) => { + const updatedSources: string[] = []; + for (const path of sources) { + if (path === source) { + continue; + } + updatedSources.push(path); + } + onWillRemoveSourcePath(updatedSources); + dispatch(updateSource(updatedSources)); + }; + + const handleAdd = () => { + onWillAddSourcePath(); + }; + + const onDidUpdateSourceFolder = (event: OnDidAddSourceFolderEvent) => { + const {data} = event; + if (data.command === "onDidUpdateSourceFolder") { + dispatch(updateSource(data.sourcePaths)); + } + }; + + useEffect(() => { + window.addEventListener("message", onDidUpdateSourceFolder); + return () => window.removeEventListener("message", onDidUpdateSourceFolder); + }, []); + + let sourceSections: JSX.Element | JSX.Element[]; + if (sources.length === 0) { + sourceSections = ( + + No source paths are configured. + + ); + } else { + sourceSections = sources.map((source) => ( + + {source} + {projectType === ProjectType.UnmanagedFolder && + + handleRemove(source)}> + + + + } + + )); + } + + return ( +
+
+

Sources

+ {projectType !== ProjectType.UnmanagedFolder && + (Read-only) + } +
+ Specify the source locations. + + + Path + + {sourceSections} + + {projectType === ProjectType.UnmanagedFolder && + handleAdd()}>Add + } +
+ ); +}; + +interface OnDidAddSourceFolderEvent { + data: { + command: string; + sourcePaths: string[]; + }; +} + +export default Sources; diff --git a/src/classpath/assets/index.html b/src/classpath/assets/index.html new file mode 100644 index 00000000..2c978dac --- /dev/null +++ b/src/classpath/assets/index.html @@ -0,0 +1,14 @@ + + + + + + + Classpath Configuration + + + +
+ + + diff --git a/src/classpath/assets/index.tsx b/src/classpath/assets/index.tsx new file mode 100644 index 00000000..28987d3e --- /dev/null +++ b/src/classpath/assets/index.tsx @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import * as React from "react"; +import * as ReactDOM from "react-dom"; +import { Provider } from "react-redux"; +import { App } from "./App"; +import store from "./app/store"; +import "./style.scss"; + +ReactDOM.render( + + + + + , + document.getElementById("content") +); diff --git a/src/classpath/assets/style.scss b/src/classpath/assets/style.scss new file mode 100644 index 00000000..da4132ff --- /dev/null +++ b/src/classpath/assets/style.scss @@ -0,0 +1,150 @@ +$enable-rounded: false; + +.root { + margin: auto; + overflow: hidden; + max-width: 720px !important; +} + +.setting-header { + padding: 15px 0px 10px 0px; +} + +.setting-section { + padding: 12px 0px 18px 0px; + + &:hover { + background-color: var(--vscode-notebook-rowHoverBackground) !important; + } +} + +.setting-section-header { + color: var(--vscode-settings-headerForeground) !important; + display: flex; + align-items: baseline; + + span { + color: var(--vscode-descriptionForeground) !important; + } +} + +.setting-section-description { + color: var(--vscode-descriptionForeground) !important; +} + +.warning { + color: var(--vscode-editorWarning-foreground); +} + +.inactive { + opacity: 0.67; +} + +.dropdown { + max-width: 320px !important; + + .dropdown-button { + color: var(--vscode-settings-dropdownForeground) !important; + background-color: var(--vscode-settings-dropdownBackground) !important; + border: 1px solid var(--vscode-settings-dropdownBorder) !important; + padding: 2px 4px !important; + width: 100%; + + &:focus, &:active { + outline: none !important; + box-shadow: none !important; + border: 1px solid var(--vscode-focusBorder) !important; + } + } + + .dropdown-menu { + width: 100%; + border: 1px solid var(--vscode-focusBorder) !important; + background-color: var(--vscode-settings-dropdownBackground) !important; + background-clip: unset !important; + } + + .dropdown-item { + background-color: var(--vscode-settings-dropdownBackground) !important; + color: var(--vscode-settings-dropdownForeground) !important; + + &:hover { + background-color: var(--vscode-list-activeSelectionBackground) !important; + color: var(--vscode-list-activeSelectionForeground) !important; + } + } +} + +div.list { + line-height: 24px; + + div.list-row-header { + font-weight: 700; + } + + div.list-row-body { + &:hover:not(.inactive) { + background-color: var(--vscode-list-hoverBackground) !important; + } + } + + .list-group-item { + border: 1px !important; + color: var(--vscode-settings-textInputForeground) !important; + background-color: transparent !important; + + &:nth-of-type(odd) { + background-color: hsla(0, 0%, 51%, .04) !important; + } + } +} + +.flex-vertical-center { + display: flex !important; + justify-content: space-between !important; + align-items: center !important; +} + +svg.codicon { + width: 1.2em; + height: 1.2em; +} + +.btn-action { + background-color: var(--vscode-button-background) !important; + color: var(--vscode-button-foreground) !important; + padding: 2px 14px !important; + border: 0 !important; + + &:hover { + color: var(--vscode-button-foreground) !important; + background-color: var(--vscode-button-hoverBackground) !important; + } +} + +body.vscode-high-contrast { + .btn-action { + border: 1px solid var(--vscode-settings-textInputBorder) !important; + } +} + +.input { + color: var(--vscode-settings-textInputForeground); + background-color: var(--vscode-settings-textInputBackground); + border: 1px solid var(--vscode-settings-textInputBorder); + height: auto; + padding: 4px; + min-width: 200px; + min-height: 26px; + display: table; +} + +.cursor-pointer { + cursor: pointer !important; +} + +.scale-up:active { + transform: scale(1.2); +} + +@import "../../assets/vscode.scss"; diff --git a/src/classpath/assets/utils.ts b/src/classpath/assets/utils.ts new file mode 100644 index 00000000..6162fdb9 --- /dev/null +++ b/src/classpath/assets/utils.ts @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { ProjectType } from "../types"; + +export const WEBVIEW_ID = "java.classpathConfiguration"; + +// RPC calls to VS Code +declare function acquireVsCodeApi(): any; +const vscode = acquireVsCodeApi && acquireVsCodeApi(); + +export function onWillLoadProjectClasspath(uri: string) { + vscode.postMessage({ + command: "onWillLoadProjectClasspath", + uri, + }); +} + +export function onWillSelectOutputPath() { + vscode.postMessage({ + command: "onWillSelectOutputPath" + }); +} + +export function onWillRemoveSourcePath(sourcePaths: string[]) { + vscode.postMessage({ + command: "onWillRemoveSourcePath", + sourcePaths, + }); +} + +export function onWillAddSourcePath() { + vscode.postMessage({ + command: "onWillAddSourcePath" + }); +} + +export function onWillAddReferencedLibraries() { + vscode.postMessage({ + command: "onWillAddReferencedLibraries" + }); +} + +export function onWillRemoveReferencedLibraries(path: string) { + vscode.postMessage({ + command: "onWillRemoveReferencedLibraries", + path, + }); +} + +export function onClickGotoProjectConfiguration(rootUri: string, projectType: ProjectType) { + vscode.postMessage({ + command: "onClickGotoProjectConfiguration", + rootUri, + projectType, + }); +} diff --git a/src/classpath/classpathConfigurationView.ts b/src/classpath/classpathConfigurationView.ts new file mode 100644 index 00000000..74c35f9e --- /dev/null +++ b/src/classpath/classpathConfigurationView.ts @@ -0,0 +1,477 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import * as vscode from "vscode"; +import * as path from "path"; +import { getExtensionContext, loadTextFromFile } from "../utils"; +import * as fse from "fs-extra"; +import { ProjectInfo, ClasspathComponent, ProjectType, ClasspathViewException } from "./types"; +import _ from "lodash"; +import minimatch from "minimatch"; +import { instrumentOperation, sendError, sendInfo, setUserError } from "vscode-extension-telemetry-wrapper"; + +let classpathConfigurationPanel: vscode.WebviewPanel | undefined; +let lsApi: LanguageServerAPI | undefined; +let currentProjectRoot: vscode.Uri; +const SOURCE_PATH_KEY: string = "org.eclipse.jdt.ls.core.sourcePaths"; +const OUTPUT_PATH_KEY: string = "org.eclipse.jdt.ls.core.defaultOutputPath"; +const REFERENCED_LIBRARIES_KEY: string = "org.eclipse.jdt.ls.core.referencedLibraries"; + +export async function showClasspathConfigurationPage(context: vscode.ExtensionContext): Promise { + if (classpathConfigurationPanel) { + classpathConfigurationPanel.reveal(); + return; + } + + classpathConfigurationPanel = vscode.window.createWebviewPanel( + "java.classpathConfiguration", + "Classpath Configuration", + vscode.ViewColumn.Active, + { + enableScripts: true, + enableCommandUris: true, + retainContextWhenHidden: true + } + ); + + await initializeWebview(context); +} + +export class ClassPathConfigurationViewSerializer implements vscode.WebviewPanelSerializer { + async deserializeWebviewPanel(webviewPanel: vscode.WebviewPanel, _state: any) { + classpathConfigurationPanel = webviewPanel; + await initializeWebview(getExtensionContext()); + } +} + +async function initializeWebview(context: vscode.ExtensionContext): Promise { + if (!classpathConfigurationPanel) { + sendError(new Error("classpathConfigurationPanel is not defined.")); + return; + } + + context.subscriptions.push(classpathConfigurationPanel.onDidDispose(_e => classpathConfigurationPanel = undefined)); + classpathConfigurationPanel.iconPath = { + light: vscode.Uri.file(path.join(context.extensionPath, "caption.light.svg")), + dark: vscode.Uri.file(path.join(context.extensionPath, "caption.dark.svg")) + }; + const resourceUri = context.asAbsolutePath("./out/assets/classpath/index.html"); + classpathConfigurationPanel.webview.html = await loadTextFromFile(resourceUri); + + if (!(await checkRequirement())) { + return; + } + await listProjects(); + + context.subscriptions.push(classpathConfigurationPanel.webview.onDidReceiveMessage((async (message) => { + switch (message.command) { + case "onWillLoadProjectClasspath": + currentProjectRoot = vscode.Uri.parse(message.uri); + await loadProjectClasspath(currentProjectRoot); + break; + case "onWillSelectOutputPath": + await setOutputPath(currentProjectRoot); + break; + case "onWillAddSourcePath": + await addSourcePath(currentProjectRoot); + break; + case "onWillRemoveSourcePath": + removeSourcePath(currentProjectRoot, message.sourcePaths); + break; + case "onWillAddReferencedLibraries": + await addReferencedLibraries(currentProjectRoot); + break; + case "onWillRemoveReferencedLibraries": + removeReferencedLibrary(currentProjectRoot, message.path); + break; + case "onClickGotoProjectConfiguration": + gotoProjectConfigurationFile(message.rootUri, message.projectType); + break; + default: + break; + } + }))); + + context.subscriptions.push(lsApi!.onDidProjectsImport(() => { + listProjects(); + })); + + context.subscriptions.push(lsApi!.onDidClasspathUpdate((uri: vscode.Uri) => { + if (!path.relative(uri.fsPath, currentProjectRoot.fsPath)) { + loadProjectClasspath(uri); + } + })); +} + +async function checkRequirement(): Promise { + const javaExt = vscode.extensions.getExtension("redhat.java"); + if (!javaExt) { + // todo: check extension version + classpathConfigurationPanel?.webview.postMessage({ + command: "onException", + exception: ClasspathViewException.JavaExtensionNotInstalled, + }); + const err: Error = new Error("redhat.java is not installed or the version is too stale."); + setUserError(err); + sendError(err); + return false; + } + await javaExt.activate(); + lsApi = javaExt.exports; + return true; +} + +const listProjects = instrumentOperation("classpath.listProjects", async (operationId: string) => { + let projects: ProjectInfo[] = await getProjectsFromLS(); + + _.remove(projects, (p: ProjectInfo) => { + return isDefaultProject(p.rootPath); + }); + + if (projects.length === 0) { + classpathConfigurationPanel?.webview.postMessage({ + command: "onException", + exception: ClasspathViewException.NoJavaProjects, + }); + } else { + classpathConfigurationPanel?.webview.postMessage({ + command: "onDidListProjects", + projectInfo: projects, + }); + } + + sendInfo(operationId, { + projectNumber: projects.length, + }); +}); + +const loadProjectClasspath = instrumentOperation("classpath.loadClasspath", async (operationId: string, currentProjectRoot: vscode.Uri) => { + const classpath = await getProjectClasspathFromLS(currentProjectRoot); + if (classpath) { + classpathConfigurationPanel?.webview.postMessage({ + command: "onDidLoadProjectClasspath", + projectType: classpath.projectType, + sources: classpath.sourcePaths, + output: classpath.defaultOutputPath, + referencedLibraries: classpath.referenceLibraries + }); + } + + sendInfo(operationId, { + projectType: classpath.projectType, + }); +}); + +const addSourcePath = instrumentOperation("classpath.addSourcePath", async (_operationId: string, currentProjectRoot: vscode.Uri) => { + const sourceFolder: vscode.Uri[] | undefined = await vscode.window.showOpenDialog({ + defaultUri: vscode.workspace.workspaceFolders?.[0].uri, + openLabel: "Select Source Folder", + canSelectFiles: false, + canSelectFolders: true, + canSelectMany: false, + }); + if (sourceFolder) { + const sourceFolderPath: string = sourceFolder[0].fsPath; + const projectRootPath: string = currentProjectRoot.fsPath; + let relativePath: string = path.relative(projectRootPath, sourceFolderPath); + if (relativePath.startsWith("..")) { + const err: Error = new Error("Cannot set the source path outside the project root."); + vscode.window.showErrorMessage(err.message); + setUserError(err); + throw(err); + } + if (!relativePath) { + relativePath = "."; + } + const sourcePaths: string[] = vscode.workspace.getConfiguration("java", currentProjectRoot).get("project.sourcePaths") || []; + if (sourcePaths.includes(relativePath)) { + vscode.window.showInformationMessage(`The path ${relativePath} has already been a source path.`); + return; + } + sourcePaths.push(relativePath); + vscode.workspace.getConfiguration("java", currentProjectRoot).update( + "project.sourcePaths", + sourcePaths, + vscode.ConfigurationTarget.Workspace, + ); + classpathConfigurationPanel?.webview.postMessage({ + command: "onDidUpdateSourceFolder", + sourcePaths, + }); + } +}); + +const removeSourcePath = instrumentOperation("classpath.removeSourcePath", (_operationId: string, currentProjectRoot: vscode.Uri, sourcePaths: string[]) => { + vscode.workspace.getConfiguration("java", currentProjectRoot).update( + "project.sourcePaths", + sourcePaths, + vscode.ConfigurationTarget.Workspace + ); +}); + +const setOutputPath = instrumentOperation("classpath.setOutputPath", async (operationId: string, currentProjectRoot: vscode.Uri) => { + const outputFolder: vscode.Uri[] | undefined = await vscode.window.showOpenDialog({ + defaultUri: vscode.workspace.workspaceFolders?.[0].uri, + openLabel: "Select Output Folder", + canSelectFiles: false, + canSelectFolders: true, + canSelectMany: false, + }); + if (outputFolder) { + const projectRootPath: string = currentProjectRoot.fsPath; + const outputFullPath: string = outputFolder[0].fsPath; + const outputRelativePath: string = path.relative(projectRootPath, outputFullPath); + if (outputRelativePath.startsWith("..")) { + const err: Error = new Error("Cannot set the output path outside the project root."); + vscode.window.showErrorMessage(err.message); + setUserError(err); + throw(err); + } + if (!outputRelativePath) { + const err: Error = new Error("Cannot set the project root path as the output path."); + vscode.window.showErrorMessage(err.message); + setUserError(err); + throw(err); + } + if ((await fse.readdir(outputFullPath)).length) { + const choice: string | undefined = await vscode.window.showInformationMessage(`The contents in ${outputFullPath} will be removed, are you sure to continue?`, "Yes", "No"); + if (choice === "Yes") { + await fse.remove(outputFullPath); + await fse.ensureDir(outputFullPath); + } else { + sendInfo(operationId, { + canceled: "Cancelled for un-empty output folder", + }); + return; + } + } + vscode.workspace.getConfiguration("java", currentProjectRoot).update( + "project.outputPath", + outputRelativePath, + vscode.ConfigurationTarget.Workspace, + ); + classpathConfigurationPanel?.webview.postMessage({ + command: "onDidSelectOutputPath", + output: outputRelativePath, + }); + } +}); + +const addReferencedLibraries = instrumentOperation("classpath.addReferencedLibraries", async (_operationId: string, currentProjectRoot: vscode.Uri) => { + const jarFiles: vscode.Uri[] | undefined = await vscode.window.showOpenDialog({ + defaultUri: vscode.workspace.workspaceFolders?.[0].uri, + openLabel: "Select Jar File", + canSelectFiles: true, + canSelectFolders: false, + canSelectMany: true, + filters: { + "Jar": ["jar"], + }, + }); + if (jarFiles) { + const jarPaths: string[] = jarFiles.map(uri => { + if (uri.fsPath.startsWith(currentProjectRoot.fsPath)) { + return path.relative(currentProjectRoot.fsPath, uri.fsPath); + } + return uri.fsPath; + }); + addLibraryGlobs(jarPaths); + classpathConfigurationPanel?.webview.postMessage({ + command: "onDidAddReferencedLibraries", + jars: jarPaths, + }); + } +}); + +const removeReferencedLibrary = instrumentOperation("classpath.removeReferencedLibrary", async (_operationId: string, currentProjectRoot: vscode.Uri, removalFsPath: string) => { + if (!path.isAbsolute(removalFsPath)) { + removalFsPath = path.join(currentProjectRoot.fsPath, removalFsPath); + } + const setting = getReferencedLibrariesSetting(); + const removedPaths = _.remove(setting.include, (include) => { + if (path.isAbsolute(include)) { + return vscode.Uri.file(include).fsPath === removalFsPath; + } else { + return include === vscode.workspace.asRelativePath(removalFsPath, false); + } + }); + if (removedPaths.length === 0) { + // No duplicated item in include array, add it into the exclude field + setting.exclude = updatePatternArray(setting.exclude, vscode.workspace.asRelativePath(removalFsPath, false)); + } + updateReferencedLibraries(setting); +}); + +const gotoProjectConfigurationFile = instrumentOperation("classpath.gotoProjectConfigurationFile", (operationId: string, rootUri: string, projectType: ProjectType) => { + const rootPath: string = vscode.Uri.parse(rootUri).fsPath; + let configurationPath: string = ""; + if (projectType === ProjectType.Gradle) { + configurationPath = path.join(rootPath, "build.gradle"); + } else if (projectType === ProjectType.Maven) { + configurationPath = path.join(rootPath, "pom.xml"); + } + + if (!configurationPath) { + const err: Error = new Error(`The configuration file: '${configurationPath}' does not exist.`); + vscode.window.showErrorMessage(err.message); + throw(err); + } + vscode.commands.executeCommand("vscode.open", vscode.Uri.file(configurationPath)); + sendInfo(operationId, { + projectType, + }); +}); + +async function getProjectsFromLS(): Promise { + const ret: ProjectInfo[] = []; + let projects: string[] = []; + try { + projects = await vscode.commands.executeCommand("java.execute.workspaceCommand", "java.project.getAll") || []; + } catch (error) { + // LS not ready + } + + for (const projectRoot of projects) { + ret.push({ + name: path.basename(projectRoot), + rootPath: projectRoot, + }); + } + return ret; +} + +async function getProjectClasspathFromLS(uri: vscode.Uri): Promise { + const queryKeys: string[] = [ + SOURCE_PATH_KEY, + OUTPUT_PATH_KEY, + REFERENCED_LIBRARIES_KEY + ]; + + const response = await lsApi!.getProjectSettings( + uri.toString(), + queryKeys + ); + const classpath: ClasspathComponent = { + projectType: await getProjectType(uri.fsPath), + sourcePaths: response[SOURCE_PATH_KEY] as string[], + defaultOutputPath: response[OUTPUT_PATH_KEY] as string, + referenceLibraries: response[REFERENCED_LIBRARIES_KEY] as string[], + }; + const baseFsPath = uri.fsPath; + + classpath.sourcePaths = classpath.sourcePaths.map(p => { + const relativePath: string = path.relative(baseFsPath, p); + if (!relativePath) { + return "."; + } + return relativePath; + }).sort((srcA: string, srcB: string) => { + return srcA.localeCompare(srcB); + }); + + const outputRelativePath: string = path.relative(baseFsPath, classpath.defaultOutputPath); + if (!outputRelativePath.startsWith("..")) { + classpath.defaultOutputPath = path.relative(baseFsPath, classpath.defaultOutputPath); + } + + classpath.referenceLibraries = classpath.referenceLibraries.map(p => { + const normalizedPath: string = vscode.Uri.file(p).fsPath; + if (normalizedPath.startsWith(baseFsPath)) { + return path.relative(baseFsPath, normalizedPath); + } + return normalizedPath; + }).sort((libA: string, libB: string) => { + // relative paths come first + const isAbsolutePathForA: boolean = path.isAbsolute(libA); + const isAbsolutePathForB: boolean = path.isAbsolute(libB); + if (isAbsolutePathForA && !isAbsolutePathForB) { + return 1; + } else if (!isAbsolutePathForA && isAbsolutePathForB) { + return -1; + } else { + return libA.localeCompare(libB); + } + }); + return classpath; +} + +async function getProjectType(fsPath: string): Promise { + if (isDefaultProject(fsPath)) { + return ProjectType.Default; + } + const dotProjectFile = path.join(fsPath, ".project"); + if (!await fse.pathExists(dotProjectFile)) { // for invisible projects, .project file is located in workspace storage. + return ProjectType.UnmanagedFolder; + } + const buildDotGradleFile = path.join(fsPath, "build.gradle"); + if (await fse.pathExists(buildDotGradleFile)) { + return ProjectType.Gradle; + } + const pomDotXmlFile = path.join(fsPath, "pom.xml"); + if (await fse.pathExists(pomDotXmlFile)) { + return ProjectType.Maven; + } + return ProjectType.Others; +} + +function isDefaultProject(path: string): boolean { + return path.indexOf("jdt.ls-java-project") > -1; +} + +function getReferencedLibrariesSetting(): IReferencedLibraries { + const setting = vscode.workspace.getConfiguration("java.project").get>("referencedLibraries"); + const defaultSetting: IReferencedLibraries = { include: [], exclude: [], sources: {} }; + if (Array.isArray(setting)) { + return { ...defaultSetting, include: setting }; + } else { + return { ...defaultSetting, ...setting }; + } +} + +function updateReferencedLibraries(libraries: IReferencedLibraries): void { + let updateSetting: string[] | Partial = { + include: libraries.include, + exclude: libraries.exclude.length > 0 ? libraries.exclude : undefined, + sources: Object.keys(libraries.sources).length > 0 ? libraries.sources : undefined, + }; + if (!updateSetting.exclude && !updateSetting.sources) { + updateSetting = libraries.include; + } + vscode.workspace.getConfiguration().update("java.project.referencedLibraries", updateSetting); +} + +function addLibraryGlobs(libraryGlobs: string[]) { + const setting = getReferencedLibrariesSetting(); + setting.exclude = dedupAlreadyCoveredPattern(libraryGlobs, ...setting.exclude); + setting.include = updatePatternArray(setting.include, ...libraryGlobs); + updateReferencedLibraries(setting); +} + +/** + * Check if the `update` patterns are already covered by `origin` patterns and return those uncovered + */ +function dedupAlreadyCoveredPattern(origin: string[], ...update: string[]): string[] { + return update.filter((newPattern) => { + return !origin.some((originPattern) => { + return minimatch(newPattern, originPattern); + }); + }); +} + +function updatePatternArray(origin: string[], ...update: string[]): string[] { + update = dedupAlreadyCoveredPattern(origin, ...update); + origin.push(...update); + return _.uniq(origin); +} + +interface LanguageServerAPI { + onDidProjectsImport: vscode.Event; + onDidClasspathUpdate: vscode.Event; + getProjectSettings: (uri: string, SettingKeys: string[]) => Promise; +} + +interface IReferencedLibraries { + include: string[]; + exclude: string[]; + sources: { [binary: string]: string }; +} diff --git a/src/classpath/types.ts b/src/classpath/types.ts new file mode 100644 index 00000000..775e790d --- /dev/null +++ b/src/classpath/types.ts @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +export interface ProjectInfo { + name: string; + rootPath: string; +} + +export interface ClasspathComponent { + projectType: ProjectType; + sourcePaths: string[]; + defaultOutputPath: string; + referenceLibraries: string[]; +} + +export enum ProjectType { + Default = "Default project", + UnmanagedFolder = "Unmanaged folder", + Maven = "Maven", + Gradle = "Gradle", + Others = "Others", +} + +export enum NatureId { + Maven = "org.eclipse.m2e.core.maven2Nature", + Gradle = "org.eclipse.buildship.core.gradleprojectnature", + Java = "org.eclipse.jdt.core.javanature", +} + +export enum ClasspathViewException { + JavaExtensionNotInstalled = "javaExtensionNotInstalled", + NoJavaProjects = "noJavaProjects", +} diff --git a/src/commands/index.ts b/src/commands/index.ts index d35997cc..385c5d3b 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -2,7 +2,6 @@ // Licensed under the MIT license. import * as vscode from "vscode"; - import { instrumentCommand, webviewCmdLinkHandler } from "../utils"; import { createMavenProjectCmdHandler, createSpringBootProjectCmdHandler, createQuarkusProjectCmdHandler, createMicroProfileStarterProjectCmdHandler, showExtensionCmdHandler, openUrlCmdHandler, showReleaseNotesHandler, installExtensionCmdHandler } from "./handler"; import { overviewCmdHandler } from "../overview"; @@ -11,6 +10,7 @@ import { javaGettingStartedCmdHandler } from "../getting-started"; import { javaExtGuideCmdHandler } from "../ext-guide"; import { instrumentOperationAsVsCodeCommand } from "vscode-extension-telemetry-wrapper"; import { showWelcomeWebview } from "../welcome"; +import { showClasspathConfigurationPage } from "../classpath/classpathConfigurationView"; export function initialize(context: vscode.ExtensionContext) { context.subscriptions.push(vscode.commands.registerCommand("java.overview", instrumentCommand(context, "java.overview", instrumentCommand(context, "java.helper.overview", overviewCmdHandler)))); @@ -27,4 +27,5 @@ export function initialize(context: vscode.ExtensionContext) { context.subscriptions.push(vscode.commands.registerCommand("java.extGuide", instrumentCommand(context, "java.extGuide", javaExtGuideCmdHandler))); context.subscriptions.push(instrumentOperationAsVsCodeCommand("java.webview.runCommand", webviewCmdLinkHandler)); context.subscriptions.push(vscode.commands.registerCommand("java.welcome", (options) => showWelcomeWebview(context, options))); + context.subscriptions.push(vscode.commands.registerCommand("java.classpathConfiguration", () => showClasspathConfigurationPage(context))); } diff --git a/src/extension.ts b/src/extension.ts index d3d310cc..a7540101 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -15,6 +15,7 @@ import { scheduleAction } from "./utils/scheduler"; import { showWelcomeWebview, WelcomeViewSerializer } from "./welcome"; import { JavaGettingStartedViewSerializer } from "./getting-started"; import { JavaExtGuideViewSerializer } from "./ext-guide"; +import { ClassPathConfigurationViewSerializer } from "./classpath/classpathConfigurationView"; export async function activate(context: vscode.ExtensionContext) { syncState(context); @@ -35,6 +36,7 @@ async function initializeExtension(_operationId: string, context: vscode.Extensi context.subscriptions.push(vscode.window.registerWebviewPanelSerializer("java.runtime", new JavaRuntimeViewSerializer())); context.subscriptions.push(vscode.window.registerWebviewPanelSerializer("java.gettingStarted", new JavaGettingStartedViewSerializer())); context.subscriptions.push(vscode.window.registerWebviewPanelSerializer("java.welcome", new WelcomeViewSerializer())); + context.subscriptions.push(vscode.window.registerWebviewPanelSerializer("java.classpathConfiguration", new ClassPathConfigurationViewSerializer())); const config = vscode.workspace.getConfiguration("java.help"); diff --git a/webpack.config.js b/webpack.config.js index f925fe47..bd9e8332 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -14,6 +14,7 @@ module.exports = function (env, argv) { 'getting-started': './src/getting-started/assets/index.ts', 'ext-guide': './src/ext-guide/assets/index.ts', welcome: './src/welcome/assets/index.ts', + classpath: './src/classpath/assets/index.tsx' }, module: { rules: [{ @@ -81,6 +82,12 @@ module.exports = function (env, argv) { inlineSource: '.(js|css)$', chunks: ['welcome'] }), + new HtmlWebpackPlugin({ + filename: path.resolve(__dirname, 'out/assets/classpath/index.html'), + template: 'src/classpath/assets/index.html', + inlineSource: '.(js|css)$', + chunks: ['classpath'] + }), new HtmlWebpackInlineSourcePlugin(), ], devtool: 'source-map',