diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..77738287 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +dist/ \ No newline at end of file diff --git a/docs/collection-files.md b/docs/collection-files.md index 662f1f8a..1a270e29 100644 --- a/docs/collection-files.md +++ b/docs/collection-files.md @@ -41,6 +41,9 @@ - `children: [` ***[`Category`](#Category)*** `|` [***`Script`***](#Script) `, ... ]` (**required**) - ❗ Category must consist of at least one subcategory or script. - Children can be combination of scripts and subcategories. +- `docs`: *`string`* | `[`*`string`*`, ... ]` + - Documentation pieces related to the category. + - Rendered as markdown. ### `Script` @@ -71,8 +74,8 @@ - A shared function or sequence of functions to call (called in order) - ❗ If not defined `code` must be defined - `docs`: *`string`* | `[`*`string`*`, ... ]` - - Single documentation URL or list of URLs for those who wants to learn more about the script - - E.g. `https://docs.microsoft.com/en-us/windows-server/` + - Documentation pieces related to the script. + - Rendered as markdown. - `recommend`: `"standard"` | `"strict"` | `undefined` (default) - If not defined then the script will not be recommended - If defined it can be either diff --git a/package-lock.json b/package-lock.json index 5f5f7bff..e8a7206c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "file-saver": "^2.0.5", "install": "^0.13.0", "liquor-tree": "^0.2.70", + "markdown-it": "^13.0.1", "npm": "^8.5.3", "v-tooltip": "2.1.3", "vue": "^2.6.14", @@ -1822,6 +1823,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, "optional": true, "engines": { "node": ">=0.1.90" @@ -2153,7 +2155,8 @@ "node_modules/@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", - "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==" + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "dev": true }, "node_modules/@hapi/address": { "version": "2.1.4", @@ -2404,6 +2407,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "dev": true, "dependencies": { "@gar/promisify": "^1.0.1", "semver": "^7.3.5" @@ -2413,6 +2417,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "dev": true, "dependencies": { "mkdirp": "^1.0.4", "rimraf": "^3.0.2" @@ -2425,6 +2430,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, "bin": { "mkdirp": "bin/cmd.js" }, @@ -4274,7 +4280,8 @@ "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true }, "node_modules/accepts": { "version": "1.3.8", @@ -4389,6 +4396,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" @@ -4519,6 +4527,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "engines": { "node": ">=8" } @@ -4527,6 +4536,7 @@ "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==", + "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -4652,8 +4662,7 @@ "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/aria-query": { "version": "5.0.2", @@ -5067,7 +5076,8 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "node_modules/base": { "version": "0.11.2", @@ -5408,6 +5418,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5790,6 +5801,7 @@ "version": "15.3.0", "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "dev": true, "dependencies": { "@npmcli/fs": "^1.0.0", "@npmcli/move-file": "^1.0.1", @@ -5818,6 +5830,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, "bin": { "mkdirp": "bin/cmd.js" }, @@ -6005,6 +6018,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -6109,6 +6123,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, "engines": { "node": ">=10" } @@ -6234,6 +6249,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, "engines": { "node": ">=6" } @@ -6369,6 +6385,7 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", + "dev": true, "dependencies": { "string-width": "^4.2.0" }, @@ -6487,6 +6504,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -6494,7 +6512,8 @@ "node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, "node_modules/colord": { "version": "2.9.3", @@ -6616,7 +6635,8 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true }, "node_modules/concat-stream": { "version": "2.0.0", @@ -8971,6 +8991,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, "engines": { "node": ">=0.8.0" } @@ -10897,6 +10918,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, "dependencies": { "minipass": "^3.0.0" }, @@ -10949,7 +10971,8 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true }, "node_modules/fsevents": { "version": "2.3.2", @@ -11122,6 +11145,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -11274,7 +11298,8 @@ "node_modules/graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true }, "node_modules/graceful-readlink": { "version": "1.0.1", @@ -11381,6 +11406,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, "engines": { "node": ">=4" } @@ -11579,6 +11605,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -12003,6 +12030,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, "engines": { "node": ">=0.8.19" } @@ -12011,6 +12039,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, "engines": { "node": ">=8" } @@ -12018,12 +12047,14 @@ "node_modules/infer-owner": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==" + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -12032,12 +12063,14 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, "node_modules/ini": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true, "engines": { "node": ">=10" } @@ -12320,6 +12353,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "engines": { "node": ">=8" } @@ -12569,7 +12603,8 @@ "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true }, "node_modules/is-unicode-supported": { "version": "0.1.0", @@ -12652,7 +12687,8 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true }, "node_modules/isobject": { "version": "3.0.1", @@ -13072,7 +13108,8 @@ "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true }, "node_modules/json-schema": { "version": "0.4.0", @@ -13404,10 +13441,9 @@ "dev": true }, "node_modules/linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", - "dev": true, + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz", + "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==", "dependencies": { "uc.micro": "^1.0.1" } @@ -13788,6 +13824,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -13853,14 +13890,13 @@ } }, "node_modules/markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", - "dev": true, + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz", + "integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==", "dependencies": { "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", + "entities": "~3.0.1", + "linkify-it": "^4.0.1", "mdurl": "^1.0.1", "uc.micro": "^1.0.5" }, @@ -13868,6 +13904,17 @@ "markdown-it": "bin/markdown-it.js" } }, + "node_modules/markdown-it/node_modules/entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/markdownlint": { "version": "0.25.1", "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.25.1.tgz", @@ -13931,6 +13978,31 @@ "integrity": "sha512-oEacRUVeTJ5D5hW1UYd2qExYI0oELdYK72k1TKGvIeYJIbqQWAz476NAc7LNixSySUhcNl++d02DvX0ccDk9/w==", "dev": true }, + "node_modules/markdownlint/node_modules/linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "dev": true, + "dependencies": { + "uc.micro": "^1.0.1" + } + }, + "node_modules/markdownlint/node_modules/markdown-it": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, "node_modules/matcher": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", @@ -14102,8 +14174,7 @@ "node_modules/mdurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", - "dev": true + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" }, "node_modules/media-typer": { "version": "0.3.0", @@ -14808,6 +14879,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -14818,12 +14890,14 @@ "node_modules/minimist": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true }, "node_modules/minipass": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.4.tgz", "integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==", + "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -14835,6 +14909,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, "dependencies": { "minipass": "^3.0.0" }, @@ -14846,6 +14921,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, "dependencies": { "minipass": "^3.0.0" }, @@ -14857,6 +14933,7 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, "dependencies": { "minipass": "^3.0.0" }, @@ -14868,6 +14945,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" @@ -14998,6 +15076,7 @@ "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, "dependencies": { "minimist": "^1.2.6" }, @@ -15633,7 +15712,8 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "node_modules/multicast-dns": { "version": "7.2.5", @@ -15929,6 +16009,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "dev": true, "dependencies": { "abbrev": "^1.0.0" }, @@ -18625,6 +18706,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, "dependencies": { "wrappy": "1" } @@ -18674,6 +18756,7 @@ "version": "1.5.2", "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true, "bin": { "opener": "bin/opener-bin.js" } @@ -18902,6 +18985,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, "dependencies": { "aggregate-error": "^3.0.0" }, @@ -19174,6 +19258,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -20217,7 +20302,8 @@ "node_modules/promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==" + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true }, "node_modules/propose": { "version": "0.0.5", @@ -22038,6 +22124,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, "dependencies": { "glob": "^7.1.3" }, @@ -22302,6 +22389,7 @@ "version": "7.3.7", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -22651,7 +22739,8 @@ "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true }, "node_modules/sirv": { "version": "1.0.19", @@ -23088,6 +23177,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "dev": true, "dependencies": { "minipass": "^3.1.1" }, @@ -23318,6 +23408,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -23330,7 +23421,8 @@ "node_modules/string-width/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true }, "node_modules/string.prototype.trimend": { "version": "1.0.5", @@ -23364,6 +23456,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -23451,6 +23544,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -23613,6 +23707,7 @@ "version": "6.1.11", "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "dev": true, "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -23629,6 +23724,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, "bin": { "mkdirp": "bin/cmd.js" }, @@ -23739,7 +23835,8 @@ "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true }, "node_modules/thenify": { "version": "3.3.1", @@ -24391,6 +24488,7 @@ "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, "dependencies": { "is-typedarray": "^1.0.0" } @@ -24411,8 +24509,7 @@ "node_modules/uc.micro": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", - "dev": true + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" }, "node_modules/un-eval": { "version": "1.2.0", @@ -24778,6 +24875,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, "dependencies": { "unique-slug": "^2.0.0" } @@ -24786,6 +24884,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, "dependencies": { "imurmurhash": "^0.1.4" } @@ -28017,6 +28116,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -28206,12 +28306,14 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true }, "node_modules/write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, "dependencies": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", @@ -28300,7 +28402,8 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, "node_modules/yaml": { "version": "1.10.2", @@ -29768,6 +29871,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, "optional": true }, "@cypress/request": { @@ -30023,7 +30127,8 @@ "@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", - "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==" + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "dev": true }, "@hapi/address": { "version": "2.1.4", @@ -30230,6 +30335,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "dev": true, "requires": { "@gar/promisify": "^1.0.1", "semver": "^7.3.5" @@ -30239,6 +30345,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "dev": true, "requires": { "mkdirp": "^1.0.4", "rimraf": "^3.0.2" @@ -30247,7 +30354,8 @@ "mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true } } }, @@ -31766,7 +31874,8 @@ "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true }, "accepts": { "version": "1.3.8", @@ -31852,6 +31961,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, "requires": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" @@ -31945,12 +32055,14 @@ "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true }, "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==", + "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -32049,8 +32161,7 @@ "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "aria-query": { "version": "5.0.2", @@ -32362,7 +32473,8 @@ "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "base": { "version": "0.11.2", @@ -32634,6 +32746,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -32937,6 +33050,7 @@ "version": "15.3.0", "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "dev": true, "requires": { "@npmcli/fs": "^1.0.0", "@npmcli/move-file": "^1.0.1", @@ -32961,7 +33075,8 @@ "mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true } } }, @@ -33101,6 +33216,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -33175,7 +33291,8 @@ "chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true }, "chrome-trace-event": { "version": "1.0.3", @@ -33277,7 +33394,8 @@ "clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true }, "cli-boxes": { "version": "2.2.1", @@ -33369,6 +33487,7 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", + "dev": true, "requires": { "@colors/colors": "1.5.0", "string-width": "^4.2.0" @@ -33460,6 +33579,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "requires": { "color-name": "1.1.3" } @@ -33467,7 +33587,8 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, "colord": { "version": "2.9.3", @@ -33570,7 +33691,8 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true }, "concat-stream": { "version": "2.0.0", @@ -35423,7 +35545,8 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true }, "escodegen": { "version": "2.0.0", @@ -36901,6 +37024,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, "requires": { "minipass": "^3.0.0" } @@ -36952,7 +37076,8 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true }, "fsevents": { "version": "2.3.2", @@ -37079,6 +37204,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -37192,7 +37318,8 @@ "graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true }, "graceful-readlink": { "version": "1.0.1", @@ -37272,7 +37399,8 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true }, "has-property-descriptors": { "version": "1.0.0", @@ -37418,6 +37546,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, "requires": { "lru-cache": "^6.0.0" } @@ -37729,22 +37858,26 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==" + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true }, "indent-string": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true }, "infer-owner": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==" + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -37753,12 +37886,14 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, "ini": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==" + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true }, "install": { "version": "0.13.0", @@ -37962,7 +38097,8 @@ "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true }, "is-glob": { "version": "4.0.3", @@ -38125,7 +38261,8 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true }, "is-unicode-supported": { "version": "0.1.0", @@ -38184,7 +38321,8 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true }, "isobject": { "version": "3.0.1", @@ -38518,7 +38656,8 @@ "json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true }, "json-schema": { "version": "0.4.0", @@ -38799,10 +38938,9 @@ "dev": true }, "linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", - "dev": true, + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz", + "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==", "requires": { "uc.micro": "^1.0.1" } @@ -39101,6 +39239,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "requires": { "yallist": "^4.0.0" } @@ -39147,16 +39286,22 @@ } }, "markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", - "dev": true, + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz", + "integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==", "requires": { "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", + "entities": "~3.0.1", + "linkify-it": "^4.0.1", "mdurl": "^1.0.1", "uc.micro": "^1.0.5" + }, + "dependencies": { + "entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==" + } } }, "markdownlint": { @@ -39166,6 +39311,30 @@ "dev": true, "requires": { "markdown-it": "12.3.2" + }, + "dependencies": { + "linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "dev": true, + "requires": { + "uc.micro": "^1.0.1" + } + }, + "markdown-it": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "dev": true, + "requires": { + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + } + } } }, "markdownlint-cli": { @@ -39343,8 +39512,7 @@ "mdurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", - "dev": true + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" }, "media-typer": { "version": "0.3.0", @@ -39779,6 +39947,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -39786,12 +39955,14 @@ "minimist": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true }, "minipass": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.4.tgz", "integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==", + "dev": true, "requires": { "yallist": "^4.0.0" } @@ -39800,6 +39971,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, "requires": { "minipass": "^3.0.0" } @@ -39808,6 +39980,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, "requires": { "minipass": "^3.0.0" } @@ -39816,6 +39989,7 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, "requires": { "minipass": "^3.0.0" } @@ -39824,6 +39998,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, "requires": { "minipass": "^3.0.0", "yallist": "^4.0.0" @@ -39945,6 +40120,7 @@ "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, "requires": { "minimist": "^1.2.6" } @@ -40445,7 +40621,8 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "multicast-dns": { "version": "7.2.5", @@ -40705,6 +40882,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "dev": true, "requires": { "abbrev": "^1.0.0" } @@ -42543,6 +42721,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, "requires": { "wrappy": "1" } @@ -42578,7 +42757,8 @@ "opener": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", - "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==" + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true }, "optionator": { "version": "0.9.1", @@ -42745,6 +42925,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, "requires": { "aggregate-error": "^3.0.0" } @@ -42976,7 +43157,8 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true }, "path-key": { "version": "3.1.1", @@ -43685,7 +43867,8 @@ "promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==" + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true }, "propose": { "version": "0.0.5", @@ -45117,6 +45300,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, "requires": { "glob": "^7.1.3" } @@ -45304,6 +45488,7 @@ "version": "7.3.7", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, "requires": { "lru-cache": "^6.0.0" } @@ -45596,7 +45781,8 @@ "signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true }, "sirv": { "version": "1.0.19", @@ -45963,6 +46149,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "dev": true, "requires": { "minipass": "^3.1.1" } @@ -46161,6 +46348,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -46170,7 +46358,8 @@ "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true } } }, @@ -46200,6 +46389,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "requires": { "ansi-regex": "^5.0.1" } @@ -46257,6 +46447,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -46380,6 +46571,7 @@ "version": "6.1.11", "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "dev": true, "requires": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -46392,7 +46584,8 @@ "mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true } } }, @@ -46468,7 +46661,8 @@ "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true }, "thenify": { "version": "3.3.1", @@ -46980,6 +47174,7 @@ "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, "requires": { "is-typedarray": "^1.0.0" } @@ -46993,8 +47188,7 @@ "uc.micro": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", - "dev": true + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" }, "un-eval": { "version": "1.2.0", @@ -47258,6 +47452,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, "requires": { "unique-slug": "^2.0.0" } @@ -47266,6 +47461,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, "requires": { "imurmurhash": "^0.1.4" } @@ -49808,6 +50004,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "requires": { "isexe": "^2.0.0" } @@ -49959,12 +50156,14 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true }, "write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, "requires": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", @@ -50024,7 +50223,8 @@ "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, "yaml": { "version": "1.10.2", diff --git a/package.json b/package.json index 8a7effb5..dd77845e 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "file-saver": "^2.0.5", "install": "^0.13.0", "liquor-tree": "^0.2.70", + "markdown-it": "^13.0.1", "npm": "^8.5.3", "v-tooltip": "2.1.3", "vue": "^2.6.14", diff --git a/src/application/Parser/CategoryParser.ts b/src/application/Parser/CategoryParser.ts index a83fd830..10f8d2ef 100644 --- a/src/application/Parser/CategoryParser.ts +++ b/src/application/Parser/CategoryParser.ts @@ -5,7 +5,7 @@ import { Script } from '@/domain/Script'; import { Category } from '@/domain/Category'; import { NodeValidator } from '@/application/Parser/NodeValidation/NodeValidator'; import { NodeType } from '@/application/Parser/NodeValidation/NodeType'; -import { parseDocUrls } from './DocumentationParser'; +import { parseDocs } from './DocumentationParser'; import { ICategoryCollectionParseContext } from './Script/ICategoryCollectionParseContext'; import { parseScript } from './Script/ScriptParser'; @@ -50,7 +50,7 @@ function parseCategoryRecursively(context: ICategoryParseContext): Category { return context.factory( /* id: */ categoryIdCounter++, /* name: */ context.categoryData.category, - /* docs: */ parseDocUrls(context.categoryData), + /* docs: */ parseDocs(context.categoryData), /* categories: */ children.subCategories, /* scripts: */ children.subScripts, ); diff --git a/src/application/Parser/DocumentationParser.ts b/src/application/Parser/DocumentationParser.ts index a3a9a931..0639dfad 100644 --- a/src/application/Parser/DocumentationParser.ts +++ b/src/application/Parser/DocumentationParser.ts @@ -1,64 +1,58 @@ -import type { DocumentableData, DocumentationUrlsData } from '@/application/collections/'; +import type { DocumentableData, DocumentationData } from '@/application/collections/'; -export function parseDocUrls(documentable: DocumentableData): ReadonlyArray { +export function parseDocs(documentable: DocumentableData): readonly string[] { if (!documentable) { throw new Error('missing documentable'); } const { docs } = documentable; - if (!docs || !docs.length) { + if (!docs) { return []; } - let result = new DocumentationUrlContainer(); + let result = new DocumentationContainer(); result = addDocs(docs, result); return result.getAll(); } function addDocs( - docs: DocumentationUrlsData, - urls: DocumentationUrlContainer, -): DocumentationUrlContainer { + docs: DocumentationData, + container: DocumentationContainer, +): DocumentationContainer { if (docs instanceof Array) { - urls.addUrls(docs); + if (docs.length > 0) { + container.addParts(docs); + } } else if (typeof docs === 'string') { - urls.addUrl(docs); + container.addPart(docs); } else { - throw new Error('Docs field (documentation url) must a string or array of strings'); + throwInvalidType(); } - return urls; + return container; } -class DocumentationUrlContainer { - private readonly urls = new Array(); +class DocumentationContainer { + private readonly parts = new Array(); - public addUrl(url: string) { - validateUrl(url); - this.urls.push(url); + public addPart(documentation: string) { + if (!documentation) { + throw Error('missing documentation'); + } + if (typeof documentation !== 'string') { + throwInvalidType(); + } + this.parts.push(documentation); } - public addUrls(urls: readonly string[]) { - for (const url of urls) { - if (typeof url !== 'string') { - throw new Error('Docs field (documentation url) must be an array of strings'); - } - this.addUrl(url); + public addParts(parts: readonly string[]) { + for (const part of parts) { + this.addPart(part); } } public getAll(): ReadonlyArray { - return this.urls; + return this.parts; } } -function validateUrl(docUrl: string): void { - if (!docUrl) { - throw new Error('Documentation url is null or empty'); - } - if (docUrl.includes('\n')) { - throw new Error('Documentation url cannot be multi-lined.'); - } - const validUrlRegex = /(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&//=]*)/g; - const res = docUrl.match(validUrlRegex); - if (res == null) { - throw new Error(`Invalid documentation url: ${docUrl}`); - } +function throwInvalidType() { + throw new Error('docs field (documentation) must be an array of strings'); } diff --git a/src/application/Parser/Script/ScriptParser.ts b/src/application/Parser/Script/ScriptParser.ts index 9bdee0cd..99a12a13 100644 --- a/src/application/Parser/Script/ScriptParser.ts +++ b/src/application/Parser/Script/ScriptParser.ts @@ -3,7 +3,7 @@ import { Script } from '@/domain/Script'; import { RecommendationLevel } from '@/domain/RecommendationLevel'; import { IScriptCode } from '@/domain/IScriptCode'; import { ScriptCode } from '@/domain/ScriptCode'; -import { parseDocUrls } from '../DocumentationParser'; +import { parseDocs } from '../DocumentationParser'; import { createEnumParser, IEnumParser } from '../../Common/Enum'; import { NodeType } from '../NodeValidation/NodeType'; import { NodeValidator } from '../NodeValidation/NodeValidator'; @@ -23,7 +23,7 @@ export function parseScript( const script = scriptFactory( /* name: */ data.name, /* code: */ parseCode(data, context), - /* docs: */ parseDocUrls(data), + /* docs: */ parseDocs(data), /* level: */ parseLevel(data.recommend, levelParser), ); return script; diff --git a/src/application/collections/collection.yaml.d.ts b/src/application/collections/collection.yaml.d.ts index 2c3a1cf9..6e7f0bce 100644 --- a/src/application/collections/collection.yaml.d.ts +++ b/src/application/collections/collection.yaml.d.ts @@ -12,10 +12,10 @@ declare module '@/application/collections/*' { } export type CategoryOrScriptData = CategoryData | ScriptData; - export type DocumentationUrlsData = ReadonlyArray | string; + export type DocumentationData = ReadonlyArray | string; export interface DocumentableData { - readonly docs?: DocumentationUrlsData; + readonly docs?: DocumentationData; } export interface InstructionHolder { diff --git a/src/domain/Category.ts b/src/domain/Category.ts index d094bcc4..9e9e35e6 100644 --- a/src/domain/Category.ts +++ b/src/domain/Category.ts @@ -8,7 +8,7 @@ export class Category extends BaseEntity implements ICategory { constructor( id: number, public readonly name: string, - public readonly documentationUrls: ReadonlyArray, + public readonly docs: ReadonlyArray, public readonly subCategories?: ReadonlyArray, public readonly scripts?: ReadonlyArray, ) { diff --git a/src/domain/IDocumentable.ts b/src/domain/IDocumentable.ts index e21f59b6..76c253cf 100644 --- a/src/domain/IDocumentable.ts +++ b/src/domain/IDocumentable.ts @@ -1,3 +1,3 @@ export interface IDocumentable { - readonly documentationUrls: ReadonlyArray; + readonly docs: ReadonlyArray; } diff --git a/src/domain/IScript.ts b/src/domain/IScript.ts index a6d00d53..a02fcc06 100644 --- a/src/domain/IScript.ts +++ b/src/domain/IScript.ts @@ -6,7 +6,7 @@ import { IScriptCode } from './IScriptCode'; export interface IScript extends IEntity, IDocumentable { readonly name: string; readonly level?: RecommendationLevel; - readonly documentationUrls: ReadonlyArray; + readonly docs: ReadonlyArray; readonly code: IScriptCode; canRevert(): boolean; } diff --git a/src/domain/Script.ts b/src/domain/Script.ts index 1e65c7e2..6f8d5ebc 100644 --- a/src/domain/Script.ts +++ b/src/domain/Script.ts @@ -7,7 +7,7 @@ export class Script extends BaseEntity implements IScript { constructor( public readonly name: string, public readonly code: IScriptCode, - public readonly documentationUrls: ReadonlyArray, + public readonly docs: ReadonlyArray, public readonly level?: RecommendationLevel, ) { super(name); diff --git a/src/presentation/assets/icons/external-link.svg b/src/presentation/assets/icons/external-link.svg new file mode 100644 index 00000000..40bcd506 --- /dev/null +++ b/src/presentation/assets/icons/external-link.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/presentation/assets/styles/_globals.scss b/src/presentation/assets/styles/_globals.scss index ba24dfa9..6d3dcf05 100644 --- a/src/presentation/assets/styles/_globals.scss +++ b/src/presentation/assets/styles/_globals.scss @@ -10,12 +10,13 @@ box-sizing: border-box; } +$globals-color-hover: $color-primary; a { color:inherit; text-decoration: underline; cursor: pointer; @include hover-or-touch { - color: $color-primary; + color: $globals-color-hover; } } diff --git a/src/presentation/assets/styles/third-party-extensions/_tree.scss b/src/presentation/assets/styles/third-party-extensions/_tree.scss index 78a5a339..fc80dd44 100644 --- a/src/presentation/assets/styles/third-party-extensions/_tree.scss +++ b/src/presentation/assets/styles/third-party-extensions/_tree.scss @@ -19,10 +19,13 @@ $color-node-checkbox-tick-checked : $color-on-secondary; &-node { white-space: normal !important; > .tree-content { - > .tree-anchor > span { - color: $color-node-fg; - text-transform: uppercase; - font-size: 1.5em; + > .tree-anchor { + > span { + color: $color-node-fg; + text-transform: uppercase; + font-size: 1.5em; + } + display: block; // so it takes full width to allow aligning items inside } @include hover-or-touch { background: $color-node-hover-bg !important; diff --git a/src/presentation/components/Scripts/View/ScriptsTree/ScriptNodeParser.ts b/src/presentation/components/Scripts/View/ScriptsTree/ScriptNodeParser.ts index d99a543b..d099dc2e 100644 --- a/src/presentation/components/Scripts/View/ScriptsTree/ScriptNodeParser.ts +++ b/src/presentation/components/Scripts/View/ScriptsTree/ScriptNodeParser.ts @@ -64,7 +64,7 @@ function convertCategoryToNode( type: NodeType.Category, text: category.name, children, - documentationUrls: category.documentationUrls, + docs: category.docs, isReversible: children && children.every((child) => child.isReversible), }; } @@ -75,7 +75,7 @@ function convertScriptToNode(script: IScript): INode { type: NodeType.Script, text: script.name, children: undefined, - documentationUrls: script.documentationUrls, + docs: script.docs, isReversible: script.canRevert(), }; } diff --git a/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/LiquorTree/LiquorTree.d.ts b/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/LiquorTree/LiquorTree.d.ts index f5cd9011..9ec7e3ae 100644 --- a/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/LiquorTree/LiquorTree.d.ts +++ b/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/LiquorTree/LiquorTree.d.ts @@ -13,7 +13,7 @@ declare module 'liquor-tree' { } export interface ICustomLiquorTreeData { type: number; - documentationUrls: ReadonlyArray; + docs: ReadonlyArray; isReversible: boolean; } // https://github.com/amsik/liquor-tree/blob/master/src/lib/Node.js diff --git a/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/LiquorTree/NodeWrapper/NodeTranslator.ts b/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/LiquorTree/NodeWrapper/NodeTranslator.ts index bb32ecdf..464f271d 100644 --- a/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/LiquorTree/NodeWrapper/NodeTranslator.ts +++ b/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/LiquorTree/NodeWrapper/NodeTranslator.ts @@ -11,7 +11,7 @@ export function convertExistingToNode(liquorTreeNode: ILiquorTreeExistingNode): text: liquorTreeNode.data.text, // selected: liquorTreeNode.states && liquorTreeNode.states.checked, children: convertChildren(liquorTreeNode.children, convertExistingToNode), - documentationUrls: liquorTreeNode.data.documentationUrls, + docs: liquorTreeNode.data.docs, isReversible: liquorTreeNode.data.isReversible, }; } @@ -27,7 +27,7 @@ export function toNewLiquorTreeNode(node: INode): ILiquorTreeNewNode { }, children: convertChildren(node.children, toNewLiquorTreeNode), data: { - documentationUrls: node.documentationUrls, + docs: node.docs, isReversible: node.isReversible, type: node.type, }, diff --git a/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/Documentation/Documentable.vue b/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/Documentation/Documentable.vue new file mode 100644 index 00000000..c14138eb --- /dev/null +++ b/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/Documentation/Documentable.vue @@ -0,0 +1,71 @@ + + + + + diff --git a/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/Documentation/DocumentationText.vue b/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/Documentation/DocumentationText.vue new file mode 100644 index 00000000..f8c83dc6 --- /dev/null +++ b/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/Documentation/DocumentationText.vue @@ -0,0 +1,119 @@ + + + + + diff --git a/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/Documentation/MarkdownRenderer.ts b/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/Documentation/MarkdownRenderer.ts new file mode 100644 index 00000000..8edce915 --- /dev/null +++ b/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/Documentation/MarkdownRenderer.ts @@ -0,0 +1,156 @@ +import MarkdownIt from 'markdown-it'; +import Renderer from 'markdown-it/lib/renderer'; +import Token from 'markdown-it/lib/token'; + +export function createRenderer(): IRenderer { + const md = new MarkdownIt({ + linkify: true, // Auto-convert URL-like text to links. + breaks: false, // Do not convert single '\n's into
. + }); + openUrlsInNewTab(md); + return { + render: (markdown: string) => { + markdown = beatifyAutoLinks(markdown); + return md.render(markdown); + }, + }; +} + +export interface IRenderer { + render(markdown: string): string; +} + +function beatifyAutoLinks(content: string): string { + if (!content) { + return content; + } + return content.replaceAll(/(? { + return toReadableLink(urlMatch); + }); +} + +function toReadableLink(url: string): string { + const parts = new URL(url); + let displayName = toReadableHostName(parts.hostname); + const pageName = extractPageName(parts); + if (pageName) { + displayName += ` - ${truncateRight(capitalizeEachLetter(pageName), 50)}`; + } + return `[${displayName}](${parts.href})`; +} + +function toReadableHostName(hostname: string): string { + const wwwStripped = hostname.replace(/^(www\.)/, ''); + const truncated = truncateLeft(wwwStripped, 30); + return truncated; +} + +function extractPageName(parts: URL): string | undefined { + const path = toReadablePath(parts.pathname); + if (path) { + return path; + } + return toReadableQuery(parts); +} + +function toReadableQuery(parts: URL): string | undefined { + const queryValues = [...parts.searchParams.values()]; + if (queryValues.length === 0) { + return undefined; + } + return selectMostDescriptiveName(queryValues); +} + +function truncateLeft(phrase: string, threshold: number): string { + return phrase.length > threshold ? `…${phrase.substring(phrase.length - threshold, phrase.length)}` : phrase; +} + +function isDigit(value: string): boolean { + return /^\d+$/.test(value); +} + +function toReadablePath(path: string): string | undefined { + const decodedPath = decodeURI(path); // Fixes e.g. %20 to whitespaces + const pathPart = selectMostDescriptiveName(decodedPath.split('/')); + if (!pathPart) { + return undefined; + } + const extensionStripped = removeTrailingExtension(pathPart); + const humanlyTokenized = extensionStripped.replaceAll(/[-_]/g, ' '); + return humanlyTokenized; +} + +function removeTrailingExtension(value: string): string { + const parts = value.split('.'); + if (parts.length === 1) { + return value; + } + if (parts.at(-1).length > 9) { + return value; // Heuristically web file extension is no longer than 9 chars (e.g. "html") + } + return parts.slice(0, -1).join('.'); +} + +function capitalizeEachLetter(phrase: string): string { + return phrase + .split(' ') + .map((word) => capitalizeFirstLetter(word)) + .join(' '); + function capitalizeFirstLetter(str: string): string { + return str.charAt(0).toUpperCase() + str.slice(1); + } +} + +function truncateRight(phrase: string, threshold: number): string { + return phrase.length > threshold ? `${phrase.substring(0, threshold)}…` : phrase; +} + +function selectMostDescriptiveName(parts: readonly string[]): string | undefined { + const goodParts = parts.filter(isGoodPathPart); + if (goodParts.length === 0) { + return undefined; + } + const longestGoodPart = goodParts.reduce((a, b) => (a.length > b.length ? a : b)); + return longestGoodPart; +} + +function isGoodPathPart(part: string): boolean { + return part + && !isDigit(part) // E.g. article numbers, issue numbers + && part.length > 2 // This is often non-human categories like T5 etc. + && !/^index(?:\.\S{0,10}$|$)/.test(part) // E.g. index.html + && !/^[A-Za-z]{2,4}([_-][A-Za-z]{4})?([_-]([A-Za-z]{2}|[0-9]{3}))$/.test(part) // Locale string e.g. fr-FR + && !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(part) // GUID + && !/^[0-9a-f]{40}$/.test(part); // Git SHA (e.g. GitHub links) +} + +function openUrlsInNewTab(md: MarkdownIt) { + // https://github.com/markdown-it/markdown-it/blob/12.2.0/docs/architecture.md#renderer + const defaultRender = getDefaultRenderer(md, 'link_open'); + md.renderer.rules.link_open = (tokens, idx, options, env, self) => { + const token = tokens[idx]; + if (!getTokenAttributeValue(token, 'target')) { + token.attrPush(['target', '_blank']); + } + return defaultRender(tokens, idx, options, env, self); + }; +} + +function getDefaultRenderer(md: MarkdownIt, ruleName: string): Renderer.RenderRule { + const renderer = md.renderer.rules[ruleName]; + if (renderer) { + return renderer; + } + return (tokens, idx, options, _env, self) => { + return self.renderToken(tokens, idx, options); + }; +} + +function getTokenAttributeValue(token: Token, attributeName: string): string | undefined { + const attributeIndex = token.attrIndex(attributeName); + if (attributeIndex < 0) { + return undefined; + } + const value = token.attrs[attributeIndex][1]; + return value; +} diff --git a/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/Documentation/ToggleDocumentationButton.vue b/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/Documentation/ToggleDocumentationButton.vue new file mode 100644 index 00000000..0ed01ed7 --- /dev/null +++ b/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/Documentation/ToggleDocumentationButton.vue @@ -0,0 +1,47 @@ + + + + + diff --git a/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/DocumentationUrls.vue b/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/DocumentationUrls.vue deleted file mode 100644 index 8f1623dd..00000000 --- a/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/DocumentationUrls.vue +++ /dev/null @@ -1,45 +0,0 @@ - - - - - diff --git a/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/INode.ts b/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/INode.ts index a322dca4..40e6b31c 100644 --- a/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/INode.ts +++ b/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/INode.ts @@ -7,7 +7,7 @@ export interface INode { readonly id: string; readonly text: string; readonly isReversible: boolean; - readonly documentationUrls: ReadonlyArray; + readonly docs: ReadonlyArray; readonly children?: ReadonlyArray; readonly type: NodeType; } diff --git a/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/Node.vue b/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/Node.vue index aa690a8d..fee0e675 100644 --- a/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/Node.vue +++ b/src/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/Node.vue @@ -1,31 +1,29 @@ @@ -33,7 +31,7 @@ export default class Node extends Vue { @use "@/presentation/assets/styles/main" as *; #node { - display:flex; + display: flex; flex-direction: row; flex-wrap: wrap; .text { diff --git a/tests/integration/application/collections/NoDeadDocumentationUrls.spec.ts b/tests/integration/application/collections/NoDeadDocumentationUrls.spec.ts index d56307d0..134569ec 100644 --- a/tests/integration/application/collections/NoDeadDocumentationUrls.spec.ts +++ b/tests/integration/application/collections/NoDeadDocumentationUrls.spec.ts @@ -36,10 +36,14 @@ function collectUniqueUrls(app: IApplication): string[] { return app .collections .flatMap((a) => a.getAllScripts()) - .flatMap((script) => script.documentationUrls) + .flatMap((script) => script.docs?.flatMap((doc) => parseUrls(doc))) .filter((url, index, array) => array.indexOf(url) === index); } +function parseUrls(text: string): string[] { + return text?.match(/\bhttps?:\/\/\S+/gi) ?? []; +} + function printUrls(statuses: IUrlStatus[]): string { /* eslint-disable prefer-template */ return '\n' diff --git a/tests/integration/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/Documentation/MarkdownRenderer.spec.ts b/tests/integration/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/Documentation/MarkdownRenderer.spec.ts new file mode 100644 index 00000000..1500b4c9 --- /dev/null +++ b/tests/integration/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/Documentation/MarkdownRenderer.spec.ts @@ -0,0 +1,46 @@ +import 'mocha'; +import { expect } from 'chai'; +import { parseApplication } from '@/application/Parser/ApplicationParser'; +import { OperatingSystem } from '@/domain/OperatingSystem'; +import { createRenderer } from '@/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/Documentation/MarkdownRenderer'; + +describe('MarkdownRenderer', () => { + describe('can render all docs', () => { + // arrange + const renderer = createRenderer(); + for (const node of collectAllDocumentableNodes()) { + it(`${node.nodeLabel}`, () => { + // act + const html = renderer.render(node.docs); + // assert + expect(isValidHtml(html)); + }); + } + }); +}); + +interface IDocumentableNode { + nodeLabel: string + docs: string +} +function* collectAllDocumentableNodes(): Generator { + const app = parseApplication(); + for (const collection of app.collections) { + const documentableNodes = [ + ...collection.getAllScripts(), + ...collection.getAllCategories(), + ]; + for (const node of documentableNodes) { + const documentable: IDocumentableNode = { + nodeLabel: `${OperatingSystem[collection.os]} | ${node.name} (${node.id})`, + docs: node.docs.join('\n'), + }; + yield documentable; + } + } +} + +function isValidHtml(value: string): boolean { + const doc = new window.DOMParser().parseFromString(value, 'text/html'); + return Array.from(doc.body.childNodes).some((node) => node.nodeType === 1); +} diff --git a/tests/unit/application/Parser/CategoryParser.spec.ts b/tests/unit/application/Parser/CategoryParser.spec.ts index 3e9d0004..5c0fb465 100644 --- a/tests/unit/application/Parser/CategoryParser.spec.ts +++ b/tests/unit/application/Parser/CategoryParser.spec.ts @@ -3,7 +3,7 @@ import { expect } from 'chai'; import type { CategoryData, CategoryOrScriptData } from '@/application/collections/'; import { CategoryFactoryType, parseCategory } from '@/application/Parser/CategoryParser'; import { parseScript } from '@/application/Parser/Script/ScriptParser'; -import { parseDocUrls } from '@/application/Parser/DocumentationParser'; +import { parseDocs } from '@/application/Parser/DocumentationParser'; import { ScriptCompilerStub } from '@tests/unit/shared/Stubs/ScriptCompilerStub'; import { ScriptDataStub } from '@tests/unit/shared/Stubs/ScriptDataStub'; import { CategoryCollectionParseContextStub } from '@tests/unit/shared/Stubs/CategoryCollectionParseContextStub'; @@ -157,14 +157,14 @@ describe('CategoryParser', () => { it('returns expected docs', () => { // arrange const url = 'https://privacy.sexy'; - const expected = parseDocUrls({ docs: url }); + const expected = parseDocs({ docs: url }); const category = new CategoryDataStub() .withDocs(url); // act const actual = new TestBuilder() .withData(category) .parseCategory() - .documentationUrls; + .docs; // assert expect(actual).to.deep.equal(expected); }); diff --git a/tests/unit/application/Parser/DocumentationParser.spec.ts b/tests/unit/application/Parser/DocumentationParser.spec.ts index b222faed..44b671d0 100644 --- a/tests/unit/application/Parser/DocumentationParser.spec.ts +++ b/tests/unit/application/Parser/DocumentationParser.spec.ts @@ -1,26 +1,60 @@ import 'mocha'; import { expect } from 'chai'; import type { DocumentableData } from '@/application/collections/'; -import { parseDocUrls } from '@/application/Parser/DocumentationParser'; -import { itEachAbsentObjectValue } from '@tests/unit/shared/TestCases/AbsentTests'; +import { parseDocs } from '@/application/Parser/DocumentationParser'; +import { itEachAbsentObjectValue, itEachAbsentStringValue } from '@tests/unit/shared/TestCases/AbsentTests'; describe('DocumentationParser', () => { - describe('parseDocUrls', () => { - describe('throws when absent', () => { + describe('parseDocs', () => { + describe('throws when node is missing', () => { itEachAbsentObjectValue((absentValue) => { // arrange const expectedError = 'missing documentable'; // act - const act = () => parseDocUrls(absentValue); + const act = () => parseDocs(absentValue); // assert expect(act).to.throw(expectedError); }); }); + describe('throws when single documentation is missing', () => { + itEachAbsentStringValue((absentValue) => { + // arrange + const expectedError = 'missing documentation'; + const node: DocumentableData = { docs: ['non empty doc 1', absentValue] }; + // act + const act = () => parseDocs(node); + // assert + expect(act).to.throw(expectedError); + }); + }); + describe('throws when type is unexpected', () => { + // arrange + const expectedTypeError = 'docs field (documentation) must be an array of strings'; + const wrongTypedValue = 22 as never; + const testCases: ReadonlyArray<{ name: string, node: DocumentableData }> = [ + { + name: 'given docs', + node: { docs: wrongTypedValue }, + }, + { + name: 'single doc', + node: { docs: ['non empty doc 1', wrongTypedValue] }, + }, + ]; + for (const testCase of testCases) { + it(testCase.name, () => { + // act + const act = () => parseDocs(testCase.node); + // assert + expect(act).to.throw(expectedTypeError); + }); + } + }); it('returns empty when empty', () => { // arrange const empty: DocumentableData = { }; // act - const actual = parseDocUrls(empty); + const actual = parseDocs(empty); // assert expect(actual).to.have.lengthOf(0); }); @@ -30,7 +64,7 @@ describe('DocumentationParser', () => { const expected = [url]; const sut: DocumentableData = { docs: url }; // act - const actual = parseDocUrls(sut); + const actual = parseDocs(sut); // assert expect(actual).to.deep.equal(expected); }); @@ -39,7 +73,7 @@ describe('DocumentationParser', () => { const expected = ['https://privacy.sexy', 'https://github.com/undergroundwires/privacy.sexy']; const sut: DocumentableData = { docs: expected }; // act - const actual = parseDocUrls(sut); + const actual = parseDocs(sut); // assert expect(actual).to.deep.equal(expected); }); diff --git a/tests/unit/application/Parser/Script/ScriptParser.spec.ts b/tests/unit/application/Parser/Script/ScriptParser.spec.ts index aa65b557..5e8f6e31 100644 --- a/tests/unit/application/Parser/Script/ScriptParser.spec.ts +++ b/tests/unit/application/Parser/Script/ScriptParser.spec.ts @@ -2,7 +2,7 @@ import 'mocha'; import { expect } from 'chai'; import type { ScriptData } from '@/application/collections/'; import { parseScript, ScriptFactoryType } from '@/application/Parser/Script/ScriptParser'; -import { parseDocUrls } from '@/application/Parser/DocumentationParser'; +import { parseDocs } from '@/application/Parser/DocumentationParser'; import { RecommendationLevel } from '@/domain/RecommendationLevel'; import { ICategoryCollectionParseContext } from '@/application/Parser/Script/ICategoryCollectionParseContext'; import { itEachAbsentObjectValue, itEachAbsentStringValue } from '@tests/unit/shared/TestCases/AbsentTests'; @@ -36,13 +36,13 @@ describe('ScriptParser', () => { const docs = ['https://expected-doc1.com', 'https://expected-doc2.com']; const script = ScriptDataStub.createWithCode() .withDocs(docs); - const expected = parseDocUrls(script); + const expected = parseDocs(script); // act const actual = new TestBuilder() .withData(script) .parseScript(); // assert - expect(actual.documentationUrls).to.deep.equal(expected); + expect(actual.docs).to.deep.equal(expected); }); describe('level', () => { describe('accepts absent level', () => { diff --git a/tests/unit/domain/Script.spec.ts b/tests/unit/domain/Script.spec.ts index 6091770a..5e3043a7 100644 --- a/tests/unit/domain/Script.spec.ts +++ b/tests/unit/domain/Script.spec.ts @@ -92,15 +92,15 @@ describe('Script', () => { } }); }); - describe('documentationUrls', () => { + describe('docs', () => { it('sets as expected', () => { // arrange const expected = ['doc1', 'doc2']; // act const sut = new ScriptBuilder() - .withDocumentationUrls(expected) + .withDocs(expected) .build(); - const actual = sut.documentationUrls; + const actual = sut.docs; // assert expect(actual).to.equal(expected); }); @@ -115,7 +115,7 @@ class ScriptBuilder { private level = RecommendationLevel.Standard; - private documentationUrls: readonly string[]; + private docs: readonly string[] = undefined; public withCodes(code: string, revertCode = ''): ScriptBuilder { this.code = new ScriptCodeStub() @@ -139,8 +139,8 @@ class ScriptBuilder { return this; } - public withDocumentationUrls(urls: readonly string[]): ScriptBuilder { - this.documentationUrls = urls; + public withDocs(urls: readonly string[]): ScriptBuilder { + this.docs = urls; return this; } @@ -148,7 +148,7 @@ class ScriptBuilder { return new Script( this.name, this.code, - this.documentationUrls, + this.docs, this.level, ); } diff --git a/tests/unit/presentation/components/Scripts/View/Tree/ScriptNodeParser.spec.ts b/tests/unit/presentation/components/Scripts/View/Tree/ScriptNodeParser.spec.ts index 81bfc6b2..324836b3 100644 --- a/tests/unit/presentation/components/Scripts/View/Tree/ScriptNodeParser.spec.ts +++ b/tests/unit/presentation/components/Scripts/View/Tree/ScriptNodeParser.spec.ts @@ -90,7 +90,7 @@ function isReversible(category: ICategory): boolean { function expectSameCategory(node: INode, category: ICategory): void { expect(node.type).to.equal(NodeType.Category, getErrorMessage('type')); expect(node.id).to.equal(getCategoryNodeId(category), getErrorMessage('id')); - expect(node.documentationUrls).to.equal(category.documentationUrls, getErrorMessage('documentationUrls')); + expect(node.docs).to.equal(category.docs, getErrorMessage('docs')); expect(node.text).to.equal(category.name, getErrorMessage('name')); expect(node.isReversible).to.equal(isReversible(category), getErrorMessage('isReversible')); expect(node.children).to.have.lengthOf(category.scripts.length || category.subCategories.length, getErrorMessage('name')); @@ -110,7 +110,7 @@ function expectSameCategory(node: INode, category: ICategory): void { function expectSameScript(node: INode, script: IScript): void { expect(node.type).to.equal(NodeType.Script, getErrorMessage('type')); expect(node.id).to.equal(getScriptNodeId(script), getErrorMessage('id')); - expect(node.documentationUrls).to.equal(script.documentationUrls, getErrorMessage('documentationUrls')); + expect(node.docs).to.equal(script.docs, getErrorMessage('docs')); expect(node.text).to.equal(script.name, getErrorMessage('name')); expect(node.isReversible).to.equal(script.canRevert(), getErrorMessage('canRevert')); expect(node.children).to.equal(undefined); diff --git a/tests/unit/presentation/components/Scripts/View/Tree/SelectableTree/LiquorTree/NodeWrapper/NodePredicateFilter.spec.ts b/tests/unit/presentation/components/Scripts/View/Tree/SelectableTree/LiquorTree/NodeWrapper/NodePredicateFilter.spec.ts index fb3d1187..6975c01c 100644 --- a/tests/unit/presentation/components/Scripts/View/Tree/SelectableTree/LiquorTree/NodeWrapper/NodePredicateFilter.spec.ts +++ b/tests/unit/presentation/components/Scripts/View/Tree/SelectableTree/LiquorTree/NodeWrapper/NodePredicateFilter.spec.ts @@ -12,7 +12,7 @@ describe('NodePredicateFilter', () => { data: { text: 'script-text', type: NodeType.Script, - documentationUrls: [], + docs: [], isReversible: false, }, states: undefined, @@ -22,7 +22,7 @@ describe('NodePredicateFilter', () => { id: 'script', text: 'script-text', isReversible: false, - documentationUrls: [], + docs: [], children: [], type: NodeType.Script, }; @@ -54,7 +54,7 @@ function getExistingNode(): ILiquorTreeExistingNode { data: { text: 'script-text', type: NodeType.Script, - documentationUrls: [], + docs: [], isReversible: false, }, states: undefined, diff --git a/tests/unit/presentation/components/Scripts/View/Tree/SelectableTree/LiquorTree/NodeWrapper/NodeStateUpdater.spec.ts b/tests/unit/presentation/components/Scripts/View/Tree/SelectableTree/LiquorTree/NodeWrapper/NodeStateUpdater.spec.ts index beae7dcd..f9462903 100644 --- a/tests/unit/presentation/components/Scripts/View/Tree/SelectableTree/LiquorTree/NodeWrapper/NodeStateUpdater.spec.ts +++ b/tests/unit/presentation/components/Scripts/View/Tree/SelectableTree/LiquorTree/NodeWrapper/NodeStateUpdater.spec.ts @@ -32,16 +32,16 @@ describe('NodeStateUpdater', () => { // arrange const node = { id: '1', - data: { type: NodeType.Category, documentationUrls: [], isReversible: false }, + data: { type: NodeType.Category, docs: [], isReversible: false }, children: [ { id: '2', - data: { type: NodeType.Category, documentationUrls: [], isReversible: false }, + data: { type: NodeType.Category, docs: [], isReversible: false }, children: [getScriptNode('a'), getScriptNode('b')], }, { id: '3', - data: { type: NodeType.Category, documentationUrls: [], isReversible: false }, + data: { type: NodeType.Category, docs: [], isReversible: false }, children: [getScriptNode('c')], }, ], @@ -56,16 +56,16 @@ describe('NodeStateUpdater', () => { // arrange const node = { id: '1', - data: { type: NodeType.Category, documentationUrls: [], isReversible: false }, + data: { type: NodeType.Category, docs: [], isReversible: false }, children: [ { id: '2', - data: { type: NodeType.Category, documentationUrls: [], isReversible: false }, + data: { type: NodeType.Category, docs: [], isReversible: false }, children: [getScriptNode('a'), getScriptNode('b')], }, { id: '3', - data: { type: NodeType.Category, documentationUrls: [], isReversible: false }, + data: { type: NodeType.Category, docs: [], isReversible: false }, children: [getScriptNode('c')], }, ], @@ -80,16 +80,16 @@ describe('NodeStateUpdater', () => { // arrange const node = { id: '1', - data: { type: NodeType.Category, documentationUrls: [], isReversible: false }, + data: { type: NodeType.Category, docs: [], isReversible: false }, children: [ { id: '2', - data: { type: NodeType.Category, documentationUrls: [], isReversible: false }, + data: { type: NodeType.Category, docs: [], isReversible: false }, children: [getScriptNode('a'), getScriptNode('b')], }, { id: '3', - data: { type: NodeType.Category, documentationUrls: [], isReversible: false }, + data: { type: NodeType.Category, docs: [], isReversible: false }, children: [getScriptNode('c')], }, ], @@ -128,16 +128,16 @@ describe('NodeStateUpdater', () => { // arrange const node = { id: '1', - data: { type: NodeType.Category, documentationUrls: [], isReversible: false }, + data: { type: NodeType.Category, docs: [], isReversible: false }, children: [ { id: '2', - data: { type: NodeType.Category, documentationUrls: [], isReversible: false }, + data: { type: NodeType.Category, docs: [], isReversible: false }, children: [getScriptNode('a'), getScriptNode('b')], }, { id: '3', - data: { type: NodeType.Category, documentationUrls: [], isReversible: false }, + data: { type: NodeType.Category, docs: [], isReversible: false }, children: [getScriptNode('c')], }, ], @@ -152,16 +152,16 @@ describe('NodeStateUpdater', () => { // arrange const node = { id: '1', - data: { type: NodeType.Category, documentationUrls: [], isReversible: false }, + data: { type: NodeType.Category, docs: [], isReversible: false }, children: [ { id: '2', - data: { type: NodeType.Category, documentationUrls: [], isReversible: false }, + data: { type: NodeType.Category, docs: [], isReversible: false }, children: [getScriptNode('a'), getScriptNode('b')], }, { id: '3', - data: { type: NodeType.Category, documentationUrls: [], isReversible: false }, + data: { type: NodeType.Category, docs: [], isReversible: false }, children: [getScriptNode('c')], }, ], @@ -176,16 +176,16 @@ describe('NodeStateUpdater', () => { // arrange const node = { id: '1', - data: { type: NodeType.Category, documentationUrls: [], isReversible: false }, + data: { type: NodeType.Category, docs: [], isReversible: false }, children: [ { id: '2', - data: { type: NodeType.Category, documentationUrls: [], isReversible: false }, + data: { type: NodeType.Category, docs: [], isReversible: false }, children: [getScriptNode('a'), getScriptNode('b')], }, { id: '3', - data: { type: NodeType.Category, documentationUrls: [], isReversible: false }, + data: { type: NodeType.Category, docs: [], isReversible: false }, children: [getScriptNode('c')], }, ], @@ -204,7 +204,7 @@ describe('NodeStateUpdater', () => { id: scriptNodeId, data: { type: NodeType.Script, - documentationUrls: [], + docs: [], isReversible: false, }, children: [], diff --git a/tests/unit/presentation/components/Scripts/View/Tree/SelectableTree/LiquorTree/NodeWrapper/NodeTranslator.spec.ts b/tests/unit/presentation/components/Scripts/View/Tree/SelectableTree/LiquorTree/NodeWrapper/NodeTranslator.spec.ts index d71c5411..d5193aad 100644 --- a/tests/unit/presentation/components/Scripts/View/Tree/SelectableTree/LiquorTree/NodeWrapper/NodeTranslator.spec.ts +++ b/tests/unit/presentation/components/Scripts/View/Tree/SelectableTree/LiquorTree/NodeWrapper/NodeTranslator.spec.ts @@ -33,20 +33,20 @@ function getNode(): INode { text: 'parentcategory', isReversible: true, type: NodeType.Category, - documentationUrls: ['parentcategory-url1', 'parentcategory-url2'], + docs: ['parentcategory-doc1', 'parentcategory-doc2'], children: [ { id: '2', text: 'subcategory', isReversible: true, - documentationUrls: ['subcategory-url1', 'subcategory-url2'], + docs: ['subcategory-doc1', 'subcategory-doc2'], type: NodeType.Category, children: [ { id: 'script1', text: 'cool script 1', isReversible: true, - documentationUrls: ['script1url1', 'script1url2'], + docs: ['script1-doc1', 'script1-doc2'], children: [], type: NodeType.Script, }, @@ -54,7 +54,7 @@ function getNode(): INode { id: 'script2', text: 'cool script 2', isReversible: true, - documentationUrls: ['script2url1', 'script2url2'], + docs: ['script2-doc1', 'script2-doc2'], children: [], type: NodeType.Script, }], @@ -66,7 +66,7 @@ function getExpectedExistingNodeData(node: INode): ILiquorTreeNodeData { return { text: node.text, type: node.type, - documentationUrls: node.documentationUrls, + docs: node.docs, isReversible: node.isReversible, }; } @@ -74,7 +74,7 @@ function getExpectedExistingNodeData(node: INode): ILiquorTreeNodeData { function getExpectedNewNodeData(node: INode): ICustomLiquorTreeData { return { type: node.type, - documentationUrls: node.documentationUrls, + docs: node.docs, isReversible: node.isReversible, }; } diff --git a/tests/unit/presentation/components/Scripts/View/Tree/SelectableTree/Node/Documentation/MarkdownRenderer.spec.ts b/tests/unit/presentation/components/Scripts/View/Tree/SelectableTree/Node/Documentation/MarkdownRenderer.spec.ts new file mode 100644 index 00000000..d6169c44 --- /dev/null +++ b/tests/unit/presentation/components/Scripts/View/Tree/SelectableTree/Node/Documentation/MarkdownRenderer.spec.ts @@ -0,0 +1,69 @@ +import 'mocha'; +import { expect } from 'chai'; +import { createRenderer } from '@/presentation/components/Scripts/View/ScriptsTree/SelectableTree/Node/Documentation/MarkdownRenderer'; + +describe('MarkdownRenderer', () => { + describe('createRenderer', () => { + it('can create', () => { + // arrange & act + const renderer = createRenderer(); + // assert + expect(renderer !== undefined); + }); + it('opens URLs in new tab', () => { + // arrange + const renderer = createRenderer(); + const markdown = '[undergroundwires.dev](https://undergroundwires.dev)'; + // act + const htmlString = renderer.render(markdown); + // assert + const html = parseHtml(htmlString); + const aElement = html.getElementsByTagName('a')[0]; + const href = aElement.getAttribute('target'); + expect(href).to.equal('_blank'); + }); + it('does not convert single linebreak to
', () => { + // arrange + const renderer = createRenderer(); + const markdown = 'Text with\nSingle\nLinebreaks'; + // act + const htmlString = renderer.render(markdown); + // assert + const html = parseHtml(htmlString); + const totalBrElements = html.getElementsByTagName('br').length; + expect(totalBrElements).to.equal(0); + }); + it('creates links for plain URL', () => { + // arrange + const renderer = createRenderer(); + const expectedUrl = 'https://privacy.sexy/'; + const markdown = `Visit ${expectedUrl} now!`; + // act + const htmlString = renderer.render(markdown); + // assert + const html = parseHtml(htmlString); + const aElement = html.getElementsByTagName('a')[0]; + const href = aElement.getAttribute('href'); + expect(href).to.equal(expectedUrl); + }); + it('it generates beautiful labels for auto-linkified URL', () => { + // arrange + const renderer = createRenderer(); + const url = 'https://privacy.sexy'; + const expectedText = 'privacy.sexy'; + const markdown = `Visit ${url} now!`; + // act + const htmlString = renderer.render(markdown); + // assert + const html = parseHtml(htmlString); + const aElement = html.getElementsByTagName('a')[0]; + expect(aElement.text).to.equal(expectedText); + }); + }); +}); + +function parseHtml(htmlString: string): Document { + const parser = new window.DOMParser(); + const htmlDoc = parser.parseFromString(htmlString, 'text/html'); + return htmlDoc; +} diff --git a/tests/unit/presentation/components/Scripts/View/Tree/SelectableTree/Node/Reverter/ReverterFactory.spec.ts b/tests/unit/presentation/components/Scripts/View/Tree/SelectableTree/Node/Reverter/ReverterFactory.spec.ts index aa5e73f4..c521d295 100644 --- a/tests/unit/presentation/components/Scripts/View/Tree/SelectableTree/Node/Reverter/ReverterFactory.spec.ts +++ b/tests/unit/presentation/components/Scripts/View/Tree/SelectableTree/Node/Reverter/ReverterFactory.spec.ts @@ -39,7 +39,7 @@ describe('ReverterFactory', () => { id: nodeId, text: 'text', isReversible: false, - documentationUrls: [], + docs: [], children: [], type, }; diff --git a/tests/unit/shared/Stubs/CategoryDataStub.ts b/tests/unit/shared/Stubs/CategoryDataStub.ts index 03f71541..6b7e6f6e 100644 --- a/tests/unit/shared/Stubs/CategoryDataStub.ts +++ b/tests/unit/shared/Stubs/CategoryDataStub.ts @@ -1,4 +1,4 @@ -import type { CategoryData, CategoryOrScriptData, DocumentationUrlsData } from '@/application/collections/'; +import type { CategoryData, CategoryOrScriptData, DocumentationData } from '@/application/collections/'; import { ScriptDataStub } from './ScriptDataStub'; export class CategoryDataStub implements CategoryData { @@ -6,7 +6,7 @@ export class CategoryDataStub implements CategoryData { public category = 'category name'; - public docs?: DocumentationUrlsData; + public docs?: DocumentationData; public withChildren(children: readonly CategoryOrScriptData[]) { this.children = children; @@ -18,7 +18,7 @@ export class CategoryDataStub implements CategoryData { return this; } - public withDocs(docs: DocumentationUrlsData) { + public withDocs(docs: DocumentationData) { this.docs = docs; return this; } diff --git a/tests/unit/shared/Stubs/CategoryStub.ts b/tests/unit/shared/Stubs/CategoryStub.ts index 7ef88fd1..8a931c44 100644 --- a/tests/unit/shared/Stubs/CategoryStub.ts +++ b/tests/unit/shared/Stubs/CategoryStub.ts @@ -9,7 +9,7 @@ export class CategoryStub extends BaseEntity implements ICategory { public readonly scripts = new Array(); - public readonly documentationUrls = new Array(); + public readonly docs = new Array(); public constructor(id: number) { super(id); diff --git a/tests/unit/shared/Stubs/ScriptDataStub.ts b/tests/unit/shared/Stubs/ScriptDataStub.ts index 723acc2a..b4edebe5 100644 --- a/tests/unit/shared/Stubs/ScriptDataStub.ts +++ b/tests/unit/shared/Stubs/ScriptDataStub.ts @@ -33,7 +33,7 @@ export class ScriptDataStub implements ScriptData { public recommend = RecommendationLevel[RecommendationLevel.Standard].toLowerCase(); - public docs = ['hello.com']; + public docs?: readonly string[] = ['hello.com']; private constructor() { /* use static methods for constructing */ } @@ -42,7 +42,7 @@ export class ScriptDataStub implements ScriptData { return this; } - public withDocs(docs: string[]): ScriptDataStub { + public withDocs(docs: readonly string[]): ScriptDataStub { this.docs = docs; return this; } diff --git a/tests/unit/shared/Stubs/ScriptStub.ts b/tests/unit/shared/Stubs/ScriptStub.ts index e2cb8c0f..6eab8de8 100644 --- a/tests/unit/shared/Stubs/ScriptStub.ts +++ b/tests/unit/shared/Stubs/ScriptStub.ts @@ -11,7 +11,7 @@ export class ScriptStub extends BaseEntity implements IScript { revert: `REM revert-code (${this.id})`, }; - public readonly documentationUrls = new Array(); + public readonly docs = new Array(); public level? = RecommendationLevel.Standard;