diff options
113 files changed, 16305 insertions, 4896 deletions
diff --git a/.gitignore b/.gitignore index fd4f2b06..6f9fe6ba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules .DS_Store +.nyc_output diff --git a/test/data-tests/index.js b/data-tests/index.js index 1b9ec990..1b9ec990 100644 --- a/test/data-tests/index.js +++ b/data-tests/index.js diff --git a/test/data-tests/test-no-short-tracks.js b/data-tests/test-no-short-tracks.js index 76356099..76356099 100644 --- a/test/data-tests/test-no-short-tracks.js +++ b/data-tests/test-no-short-tracks.js diff --git a/test/data-tests/test-order-of-album-groups.js b/data-tests/test-order-of-album-groups.js index de2fcbed..de2fcbed 100644 --- a/test/data-tests/test-order-of-album-groups.js +++ b/data-tests/test-order-of-album-groups.js diff --git a/package-lock.json b/package-lock.json index fcccac83..49852903 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,8 +11,10 @@ "dependencies": { "chroma-js": "^2.4.2", "command-exists": "^1.2.9", + "eslint": "^8.37.0", "he": "^1.2.0", "js-yaml": "^4.1.0", + "marked": "^5.0.2", "word-wrap": "^1.2.3" }, "bin": { @@ -20,20 +22,438 @@ }, "devDependencies": { "chokidar": "^3.5.3", - "eslint": "^8.18.0", - "tape": "^5.4.1" + "tap": "^16.3.4", + "tcompare": "^6.0.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", - "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", + "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.3.tgz", + "integrity": "sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.21.3", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-module-transforms": "^7.21.2", + "@babel/helpers": "^7.21.0", + "@babel/parser": "^7.21.3", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.3", + "@babel/types": "^7.21.3", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.3.tgz", + "integrity": "sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.21.3", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", + "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.20.5", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", + "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.20.7", + "@babel/types": "^7.21.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", + "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.2", + "@babel/types": "^7.21.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", + "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", + "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", + "dev": true, + "dependencies": { + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.0", + "@babel/types": "^7.21.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "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", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "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" + } + }, + "node_modules/@babel/highlight/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==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/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==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/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==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.3.tgz", + "integrity": "sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.3.tgz", + "integrity": "sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.21.3", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.21.3", + "@babel/types": "^7.21.3", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.3.tgz", + "integrity": "sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==", "dev": true, "dependencies": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.0.tgz", + "integrity": "sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.2.tgz", + "integrity": "sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==", + "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.3.2", - "globals": "^13.15.0", + "espree": "^9.5.1", + "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -42,33 +462,188 @@ }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.37.0.tgz", + "integrity": "sha512-x5vzdtOOGgFVDCUs81QRB2+liax8rFg3+7hqM+QhBG0/G3F1ZsoYl97UrqgHgQ9KKT7G6c4V+aTUCgu/n22v1A==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", - "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", - "dev": true, + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", - "minimatch": "^3.0.4" + "minimatch": "^3.0.5" }, "engines": { "node": ">=10.10.0" } }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@humanwhocodes/object-schema": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "dev": true }, - "node_modules/acorn": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", - "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", "bin": { "acorn": "bin/acorn" }, @@ -80,16 +655,27 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/aggregate-error": { + "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" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -105,7 +691,6 @@ "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" } @@ -114,7 +699,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -138,46 +722,42 @@ "node": ">= 8" } }, - "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==" - }, - "node_modules/array.prototype.every": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/array.prototype.every/-/array.prototype.every-1.1.3.tgz", - "integrity": "sha512-vWnriJI//SOMOWtXbU/VXhJ/InfnNHPF6BLKn5WfY8xXy+NWql0fUy20GO3sdqBhCAO+qw8S/E5nJiZX+QFdCA==", + "node_modules/append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "is-string": "^1.0.7" + "default-require-extensions": "^3.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "node_modules/archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", + "dev": true + }, + "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==" + }, + "node_modules/async-hook-domain": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/async-hook-domain/-/async-hook-domain-2.0.4.tgz", + "integrity": "sha512-14LjCmlK1PK8eDtTezR6WX8TMaYNIzBIsd2D1sGoGjgx0BuNMMoSdk7i/drlbtamy0AWv9yv2tkB+ASdmeqFIw==", "dev": true, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=10" } }, "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==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/binary-extensions": { "version": "2.2.0", @@ -188,11 +768,19 @@ "node": ">=8" } }, + "node_modules/bind-obj-methods": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bind-obj-methods/-/bind-obj-methods-3.0.0.tgz", + "integrity": "sha512-nLEaaz3/sEzNSyPWRsN9HNsqwk1AUyECtGj+XwGdIi3xABnEqecvXtIJ0wehQXuuER5uZ/5fTs2usONgYjG+iw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/brace-expansion": { "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" @@ -210,33 +798,92 @@ "node": ">=8" } }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "node_modules/browserslist": { + "version": "4.21.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", + "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "dependencies": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, "engines": { "node": ">=6" } }, + "node_modules/caniuse-lite": { + "version": "1.0.30001469", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001469.tgz", + "integrity": "sha512-Rcp7221ScNqQPP3W+lVOYDyjdR6dC+neEQCttoNr5bAyz54AboB4iwpnWgyi8P4YUsPybVzT4LgWiBbI3drL4g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -292,11 +939,30 @@ "resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-2.4.2.tgz", "integrity": "sha512-U9eDw6+wt7V8z5NncY2jJfZa+hUH8XEj8FQHgFJTrUFnJfXYf4Ml4adI2vXZOjqRDpFWtYVWypDfZwnJ+HIR4A==" }, + "node_modules/clean-stack": { + "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" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -307,25 +973,43 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "bin": { + "color-support": "bin.js" + } }, "node_modules/command-exists": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==" }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "dev": true }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -339,7 +1023,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -352,61 +1035,48 @@ } } }, - "node_modules/deep-equal": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.5.tgz", - "integrity": "sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "es-get-iterator": "^1.1.1", - "get-intrinsic": "^1.0.1", - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.2", - "is-regex": "^1.1.1", - "isarray": "^2.0.5", - "object-is": "^1.1.4", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "regexp.prototype.flags": "^1.3.0", - "side-channel": "^1.0.3", - "which-boxed-primitive": "^1.0.1", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" }, - "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "node_modules/default-require-extensions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", + "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", "dev": true, "dependencies": { - "object-keys": "^1.0.12" + "strip-bom": "^4.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/defined": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", - "dev": true + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, "dependencies": { "esutils": "^2.0.2" }, @@ -414,93 +1084,37 @@ "node": ">=6.0.0" } }, - "node_modules/dotignore": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/dotignore/-/dotignore-0.1.2.tgz", - "integrity": "sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw==", - "dev": true, - "dependencies": { - "minimatch": "^3.0.4" - }, - "bin": { - "ignored": "bin/ignored" - } - }, - "node_modules/es-abstract": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", - "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.1", - "is-string": "^1.0.7", - "is-weakref": "^1.0.1", - "object-inspect": "^1.11.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "node_modules/electron-to-chromium": { + "version": "1.4.340", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.340.tgz", + "integrity": "sha512-zx8hqumOqltKsv/MF50yvdAlPF9S/4PXbyfzJS6ZGhbddGkRegdwImmfSVqCkEziYzrIGZ/TlrzBND4FysfkDg==", + "dev": true }, - "node_modules/es-get-iterator": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.2.tgz", - "integrity": "sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.0", - "has-symbols": "^1.0.1", - "is-arguments": "^1.1.0", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.5", - "isarray": "^2.0.5" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "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==", + "dev": true }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6" } }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "engines": { "node": ">=10" }, @@ -509,13 +1123,17 @@ } }, "node_modules/eslint": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.18.0.tgz", - "integrity": "sha512-As1EfFMVk7Xc6/CvhssHUjsAQSkpfXvUGMFC3ce8JDe6WvqCgRrLOBQbVpsBFr1X1V+RACOadnzVvcUS5ni2bA==", - "dev": true, + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.37.0.tgz", + "integrity": "sha512-NU3Ps9nI05GUoVMxcZx1J8CNR6xOvUT4jAUMH5+z8lpp3aEdPVCImKw6PWG4PY+Vfkpr+jvMpxs/qoE7wq0sPw==", "dependencies": { - "@eslint/eslintrc": "^1.3.0", - "@humanwhocodes/config-array": "^0.9.2", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.2", + "@eslint/js": "8.37.0", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -523,20 +1141,22 @@ "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.2", - "esquery": "^1.4.0", + "eslint-visitor-keys": "^3.4.0", + "espree": "^9.5.1", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.15.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", @@ -544,11 +1164,9 @@ "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "regexpp": "^3.2.0", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" @@ -564,7 +1182,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -573,61 +1190,107 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, + "node_modules/eslint-visitor-keys": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz", + "integrity": "sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dependencies": { - "eslint-visitor-keys": "^2.0.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/mysticatea" + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dependencies": { + "p-locate": "^5.0.0" }, - "peerDependencies": { - "eslint": ">=5" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, + "node_modules/eslint/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dependencies": { + "yocto-queue": "^0.1.0" + }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true, + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dependencies": { + "p-limit": "^3.0.2" + }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/espree": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", - "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", - "dev": true, + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.1.tgz", + "integrity": "sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==", "dependencies": { - "acorn": "^8.7.1", + "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dependencies": { "estraverse": "^5.1.0" }, @@ -639,7 +1302,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, "dependencies": { "estraverse": "^5.2.0" }, @@ -651,7 +1313,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, "engines": { "node": ">=4.0" } @@ -660,34 +1321,43 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/events-to-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/events-to-array/-/events-to-array-1.1.2.tgz", + "integrity": "sha512-inRWzRY7nG+aXZxBzEqYKB3HPgwflZRopAjDCHv0whhRx+MTUr1ei0ICZUypdyE0HRm4L2d5VEcIqLD6yl+BFA==", + "dev": true + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dependencies": { + "reusify": "^1.0.4" + } }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, "dependencies": { "flat-cache": "^3.0.4" }, @@ -707,11 +1377,46 @@ "node": ">=8" } }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/findit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findit/-/findit-2.0.0.tgz", + "integrity": "sha512-ENZS237/Hr8bjczn5eKuBohLgaD0JyUd0arxretR1f9RO46vZHA1b2y0VorgGV3WaOT3c+78P8h7v4JGJ1i/rg==", + "dev": true + }, "node_modules/flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, "dependencies": { "flatted": "^3.1.0", "rimraf": "^3.0.2" @@ -723,29 +1428,51 @@ "node_modules/flatted": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", - "dev": true + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==" }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", "dev": true, "dependencies": { - "is-callable": "^1.1.3" + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8.0.0" } }, - "node_modules/foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "node_modules/fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/fs-exists-cached": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-exists-cached/-/fs-exists-cached-1.0.0.tgz", + "integrity": "sha512-kSxoARUDn4F2RPXX48UXnaFKwVU7Ivd/6qpzZL29MCDmr9sTvybv4gFCp+qaI4fM9m0z9fgz/yJvi56GAz+BZg==", "dev": true }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "node_modules/fsevents": { "version": "2.3.2", @@ -761,30 +1488,28 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "node_modules/function-loop": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/function-loop/-/function-loop-2.0.1.tgz", + "integrity": "sha512-ktIR+O6i/4h+j/ZhZJNdzeI4i9lEPeEK6UPR2EVyTVBqOwcU3Za9xYKLH64ZR9HmcROyRrOkizNyjjtWJzDDkQ==", "dev": true }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "node_modules/get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "6.* || 8.* || >= 10.*" } }, "node_modules/get-package-type": { @@ -796,32 +1521,15 @@ "node": ">=8.0.0" } }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, @@ -836,7 +1544,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, "dependencies": { "is-glob": "^4.0.3" }, @@ -845,10 +1552,9 @@ } }, "node_modules/globals": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", - "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", - "dev": true, + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "dependencies": { "type-fest": "^0.20.2" }, @@ -859,74 +1565,48 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true }, - "node_modules/has-dynamic-import": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-dynamic-import/-/has-dynamic-import-2.0.1.tgz", - "integrity": "sha512-X3fbtsZmwb6W7fJGR9o7x65fZoodygCrZ3TVycvghP62yYQfS0t4RS0Qcz+j5tQYUKeSWS09tHkWW6WhFV3XhQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } }, - "node_modules/has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "node_modules/hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", "dev": true, + "dependencies": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, "engines": { - "node": ">= 0.4" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "node_modules/hasha/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, "node_modules/he": { @@ -937,11 +1617,16 @@ "he": "bin/he" } }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true, + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "engines": { "node": ">= 4" } @@ -950,7 +1635,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -966,16 +1650,23 @@ "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" } }, + "node_modules/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -984,50 +1675,7 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/is-binary-path": { "version": "2.1.0", @@ -1041,75 +1689,27 @@ "node": ">=8" } }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -1117,27 +1717,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -1147,148 +1726,165 @@ "node": ">=0.12.0" } }, - "node_modules/is-number-object": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", - "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, "engines": { - "node": ">= 0.4" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", - "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8" } }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "node_modules/istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "append-transform": "^2.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "node_modules/istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", "dev": true, "dependencies": { - "has-symbols": "^1.0.2" + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/is-typed-array": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz", - "integrity": "sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==", + "node_modules/istanbul-lib-processinfo": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", + "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-abstract": "^1.18.5", - "foreach": "^2.0.5", - "has-tostringtag": "^1.0.0" + "archy": "^1.0.0", + "cross-spawn": "^7.0.3", + "istanbul-lib-coverage": "^3.2.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^8.3.2" }, "engines": { - "node": ">= 0.4" + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8" } }, - "node_modules/is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" } }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "node_modules/istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", "dev": true, "dependencies": { - "call-bind": "^1.0.2" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8" } }, - "node_modules/is-weakset": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "node_modules/jackspeak": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-1.4.2.tgz", + "integrity": "sha512-GHeGTmnuaHnvS+ZctRB01bfxARuu9wW83ENbuiweu07SFcVlZrJpcshSre/keGT7YGBhLHg/+rXCNSrsEHKU4Q==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "cliui": "^7.0.4" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8" } }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true + "node_modules/js-sdsl": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz", + "integrity": "sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, "node_modules/js-yaml": { @@ -1302,23 +1898,44 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -1327,17 +1944,113 @@ "node": ">= 0.8.0" } }, + "node_modules/libtap": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/libtap/-/libtap-1.4.0.tgz", + "integrity": "sha512-STLFynswQ2A6W14JkabgGetBNk6INL1REgJ9UeNKw5llXroC2cGLgKTqavv0sl8OLVztLLipVKMcQ7yeUcqpmg==", + "dev": true, + "dependencies": { + "async-hook-domain": "^2.0.4", + "bind-obj-methods": "^3.0.0", + "diff": "^4.0.2", + "function-loop": "^2.0.1", + "minipass": "^3.1.5", + "own-or": "^1.0.0", + "own-or-env": "^1.0.2", + "signal-exit": "^3.0.4", + "stack-utils": "^2.0.4", + "tap-parser": "^11.0.0", + "tap-yaml": "^1.0.0", + "tcompare": "^5.0.6", + "trivial-deferred": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/libtap/node_modules/tcompare": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/tcompare/-/tcompare-5.0.7.tgz", + "integrity": "sha512-d9iddt6YYGgyxJw5bjsN7UJUO1kGOtjSlNy/4PoGYAjQS5pAT/hzIoLf1bZCw+uUxRmZJh7Yy1aA7xKVRT9B4w==", + "dev": true, + "dependencies": { + "diff": "^4.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", + "dev": true + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lru-cache/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/marked": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-5.0.2.tgz", + "integrity": "sha512-TXksm9GwqXCRNbFUZmMtqNLvy3K2cQHuWmyBDLOrY1e6i9UvZpOTJXoz7fBjYkJkaUFzV9hBFxMuZSyQt8R6KQ==", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/minimatch": { "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" }, @@ -1345,22 +2058,56 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true + "node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "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" + }, + "engines": { + "node": ">=10" + } }, "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==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + }, + "node_modules/node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "dependencies": { + "process-on-spawn": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-releases": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", + "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", "dev": true }, "node_modules/normalize-path": { @@ -1372,72 +2119,77 @@ "node": ">=0.10.0" } }, - "node_modules/object-inspect": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", - "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "node_modules/nyc": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", + "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" }, - "engines": { - "node": ">= 0.4" + "bin": { + "nyc": "bin/nyc.js" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, "engines": { - "node": ">= 0.4" + "node": ">=8.9" } }, - "node_modules/object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "node_modules/nyc/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "dependencies": { "wrappy": "1" } }, + "node_modules/opener": { + "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" + } + }, "node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -1450,11 +2202,88 @@ "node": ">= 0.8.0" } }, + "node_modules/own-or": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/own-or/-/own-or-1.0.0.tgz", + "integrity": "sha512-NfZr5+Tdf6MB8UI9GLvKRs4cXY8/yB0w3xtt84xFdWy8hkGjn+JFc60VhzS/hFRfbyxFcGYMTjnF4Me+RbbqrA==", + "dev": true + }, + "node_modules/own-or-env": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/own-or-env/-/own-or-env-1.0.2.tgz", + "integrity": "sha512-NQ7v0fliWtK7Lkb+WdFqe6ky9XAzYmlkXthQrBbzlYbmFKoAYbDDcwmOm6q8kOuwSRXW8bdL5ORksploUJmWgw==", + "dev": true, + "dependencies": { + "own-or": "^1.0.0" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -1462,11 +2291,18 @@ "node": ">=6" } }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "engines": { + "node": ">=8" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -1475,15 +2311,14 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "engines": { "node": ">=8" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, "node_modules/picomatch": { @@ -1498,24 +2333,65 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, "engines": { "node": ">= 0.8.0" } }, + "node_modules/process-on-spawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", + "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "dev": true, + "dependencies": { + "fromentries": "^1.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, "engines": { "node": ">=6" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -1528,70 +2404,54 @@ "node": ">=8.10.0" } }, - "node_modules/regexp.prototype.flags": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz", - "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==", + "node_modules/release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "es6-error": "^4.0.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" + "node": ">=0.10.0" } }, - "node_modules/resolve": { - "version": "2.0.0-next.3", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", - "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", - "dev": true, - "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, "engines": { "node": ">=4" } }, - "node_modules/resumer": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", - "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=", - "dev": true, - "dependencies": { - "through": "~2.3.4" + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" } }, "node_modules/rimraf": { "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" }, @@ -1602,11 +2462,47 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -1618,73 +2514,97 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "engines": { "node": ">=8" } }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "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==", + "dev": true + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/string.prototype.trim": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.5.tgz", - "integrity": "sha512-Lnh17webJVsD6ECeovpVN17RlAKjmz4rF9S+8Y45CkMc/ufVpTkU3vZIyIC7sllQ1FCvObZnnCdNs/HXTUOTlg==", + "node_modules/spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "escape-string-regexp": "^2.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=10" } }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width": { + "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": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8" } }, "node_modules/strip-ansi": { "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" }, @@ -1692,11 +2612,19 @@ "node": ">=8" } }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, "engines": { "node": ">=8" }, @@ -1708,7 +2636,1221 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap": { + "version": "16.3.4", + "resolved": "https://registry.npmjs.org/tap/-/tap-16.3.4.tgz", + "integrity": "sha512-SAexdt2ZF4XBgye6TPucFI2y7VE0qeFXlXucJIV1XDPCs+iJodk0MYacr1zR6Ycltzz7PYg8zrblDXKbAZM2LQ==", + "bundleDependencies": [ + "ink", + "treport", + "@types/react", + "@isaacs/import-jsx", + "react" + ], + "dev": true, + "dependencies": { + "@isaacs/import-jsx": "^4.0.1", + "@types/react": "^17.0.52", + "chokidar": "^3.3.0", + "findit": "^2.0.0", + "foreground-child": "^2.0.0", + "fs-exists-cached": "^1.0.0", + "glob": "^7.2.3", + "ink": "^3.2.0", + "isexe": "^2.0.0", + "istanbul-lib-processinfo": "^2.0.3", + "jackspeak": "^1.4.2", + "libtap": "^1.4.0", + "minipass": "^3.3.4", + "mkdirp": "^1.0.4", + "nyc": "^15.1.0", + "opener": "^1.5.1", + "react": "^17.0.2", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.6", + "source-map-support": "^0.5.16", + "tap-mocha-reporter": "^5.0.3", + "tap-parser": "^11.0.2", + "tap-yaml": "^1.0.2", + "tcompare": "^5.0.7", + "treport": "^3.0.4", + "which": "^2.0.2" + }, + "bin": { + "tap": "bin/run.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "peerDependencies": { + "coveralls": "^3.1.1", + "flow-remove-types": ">=2.112.0", + "ts-node": ">=8.5.2", + "typescript": ">=3.7.2" + }, + "peerDependenciesMeta": { + "coveralls": { + "optional": true + }, + "flow-remove-types": { + "optional": true + }, + "ts-node": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/tap-mocha-reporter": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/tap-mocha-reporter/-/tap-mocha-reporter-5.0.3.tgz", + "integrity": "sha512-6zlGkaV4J+XMRFkN0X+yuw6xHbE9jyCZ3WUKfw4KxMyRGOpYSRuuQTRJyWX88WWuLdVTuFbxzwXhXuS2XE6o0g==", + "dev": true, + "dependencies": { + "color-support": "^1.1.0", + "debug": "^4.1.1", + "diff": "^4.0.1", + "escape-string-regexp": "^2.0.0", + "glob": "^7.0.5", + "tap-parser": "^11.0.0", + "tap-yaml": "^1.0.0", + "unicode-length": "^2.0.2" + }, + "bin": { + "tap-mocha-reporter": "index.js" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/tap-mocha-reporter/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap-parser": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-11.0.2.tgz", + "integrity": "sha512-6qGlC956rcORw+fg7Fv1iCRAY8/bU9UabUAhs3mXRH6eRmVZcNPLheSXCYaVaYeSwx5xa/1HXZb1537YSvwDZg==", + "dev": true, + "dependencies": { + "events-to-array": "^1.0.1", + "minipass": "^3.1.6", + "tap-yaml": "^1.0.0" + }, + "bin": { + "tap-parser": "bin/cmd.js" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/tap-yaml": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tap-yaml/-/tap-yaml-1.0.2.tgz", + "integrity": "sha512-GegASpuqBnRNdT1U+yuUPZ8rEU64pL35WPBpCISWwff4dErS2/438barz7WFJl4Nzh3Y05tfPidZnH+GaV1wMg==", + "dev": true, + "dependencies": { + "yaml": "^1.10.2" + } + }, + "node_modules/tap/node_modules/@ampproject/remapping": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/tap/node_modules/@babel/code-frame": { + "version": "7.16.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/compat-data": { + "version": "7.17.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/core": { + "version": "7.17.8", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.7", + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helpers": "^7.17.8", + "@babel/parser": "^7.17.8", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/tap/node_modules/@babel/generator": { + "version": "7.17.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.17.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/helper-compilation-targets": { + "version": "7.17.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-validator-option": "^7.16.7", + "browserslist": "^4.17.5", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/tap/node_modules/@babel/helper-environment-visitor": { + "version": "7.16.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/helper-function-name": { + "version": "7.16.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/helper-get-function-arity": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/helper-get-function-arity": { + "version": "7.16.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/helper-hoist-variables": { + "version": "7.16.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/helper-module-imports": { + "version": "7.16.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/helper-module-transforms": { + "version": "7.17.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/helper-plugin-utils": { + "version": "7.16.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/helper-simple-access": { + "version": "7.17.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/helper-split-export-declaration": { + "version": "7.16.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/helper-validator-identifier": { + "version": "7.16.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/helper-validator-option": { + "version": "7.16.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/helpers": { + "version": "7.17.8", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/highlight": { + "version": "7.16.10", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/parser": { + "version": "7.17.8", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/tap/node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.17.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.17.0", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/tap/node_modules/@babel/plugin-syntax-jsx": { + "version": "7.16.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/tap/node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/tap/node_modules/@babel/plugin-transform-destructuring": { + "version": "7.17.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/tap/node_modules/@babel/plugin-transform-parameters": { + "version": "7.16.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/tap/node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.17.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-jsx": "^7.16.7", + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/tap/node_modules/@babel/template": { + "version": "7.16.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/traverse": { + "version": "7.17.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.3", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/parser": "^7.17.3", + "@babel/types": "^7.17.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@babel/types": { + "version": "7.17.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/@isaacs/import-jsx": { + "version": "4.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.5.5", + "@babel/plugin-proposal-object-rest-spread": "^7.5.5", + "@babel/plugin-transform-destructuring": "^7.5.0", + "@babel/plugin-transform-react-jsx": "^7.3.0", + "caller-path": "^3.0.1", + "find-cache-dir": "^3.2.0", + "make-dir": "^3.0.2", + "resolve-from": "^3.0.0", + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tap/node_modules/@jridgewell/resolve-uri": { + "version": "3.0.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/tap/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.11", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/tap/node_modules/@types/prop-types": { + "version": "15.7.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/@types/react": { + "version": "17.0.52", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/tap/node_modules/@types/scheduler": { + "version": "0.16.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/@types/yoga-layout": { + "version": "1.9.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/ansi-escapes": { + "version": "4.3.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tap/node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "dev": true, + "inBundle": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tap/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/ansi-styles": { + "version": "3.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tap/node_modules/ansicolors": { + "version": "0.3.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/astral-regex": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/auto-bind": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tap/node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/tap/node_modules/browserslist": { + "version": "4.20.2", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "inBundle": true, + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001317", + "electron-to-chromium": "^1.4.84", + "escalade": "^3.1.1", + "node-releases": "^2.0.2", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/tap/node_modules/caller-callsite": { + "version": "4.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/caller-path": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "caller-callsite": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/callsites": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tap/node_modules/caniuse-lite": { + "version": "1.0.30001319", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ], + "inBundle": true, + "license": "CC-BY-4.0" + }, + "node_modules/tap/node_modules/cardinal": { + "version": "2.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansicolors": "~0.3.2", + "redeyed": "~2.1.0" + }, + "bin": { + "cdl": "bin/cdl.js" + } + }, + "node_modules/tap/node_modules/chalk": { + "version": "2.4.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tap/node_modules/ci-info": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/cli-boxes": { + "version": "2.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tap/node_modules/cli-cursor": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/cli-truncate": { + "version": "2.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tap/node_modules/code-excerpt": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "convert-to-spaces": "^1.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tap/node_modules/color-convert": { + "version": "1.9.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/tap/node_modules/color-name": { + "version": "1.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/commondir": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/convert-source-map": { + "version": "1.8.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/tap/node_modules/convert-to-spaces": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/tap/node_modules/csstype": { + "version": "3.0.11", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/debug": { + "version": "4.3.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/tap/node_modules/electron-to-chromium": { + "version": "1.4.89", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/tap/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/escalade": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tap/node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/tap/node_modules/esprima": { + "version": "4.0.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tap/node_modules/events-to-array": { + "version": "1.1.2", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/tap/node_modules/find-cache-dir": { + "version": "3.3.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/tap/node_modules/find-up": { + "version": "4.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/tap/node_modules/gensync": { + "version": "1.0.0-beta.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tap/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tap/node_modules/globals": { + "version": "11.12.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/tap/node_modules/has-flag": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/tap/node_modules/indent-string": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/inflight": { + "version": "1.0.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/tap/node_modules/inherits": { + "version": "2.0.4", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/tap/node_modules/ink": { + "version": "3.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.2.1", + "auto-bind": "4.0.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.0", + "cli-cursor": "^3.1.0", + "cli-truncate": "^2.1.0", + "code-excerpt": "^3.0.0", + "indent-string": "^4.0.0", + "is-ci": "^2.0.0", + "lodash": "^4.17.20", + "patch-console": "^1.0.0", + "react-devtools-core": "^4.19.1", + "react-reconciler": "^0.26.2", + "scheduler": "^0.20.2", + "signal-exit": "^3.0.2", + "slice-ansi": "^3.0.0", + "stack-utils": "^2.0.2", + "string-width": "^4.2.2", + "type-fest": "^0.12.0", + "widest-line": "^3.1.0", + "wrap-ansi": "^6.2.0", + "ws": "^7.5.5", + "yoga-layout-prebuilt": "^1.9.6" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": ">=16.8.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/tap/node_modules/ink/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/tap/node_modules/ink/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/tap/node_modules/ink/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/tap/node_modules/ink/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/ink/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/ink/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -1716,49 +3858,845 @@ "node": ">=8" } }, - "node_modules/tape": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/tape/-/tape-5.4.1.tgz", - "integrity": "sha512-7bGaJ3WnQ/CX3xOWzlR+9lNptEWoD+11gyREP8k+SYrDu2a20EifKpTmZndXn25ZRxesYHSuNtE7Fb+THcjfGA==", + "node_modules/tap/node_modules/is-ci": { + "version": "2.0.0", "dev": true, + "inBundle": true, + "license": "MIT", "dependencies": { - "array.prototype.every": "^1.1.3", - "call-bind": "^1.0.2", - "deep-equal": "^2.0.5", - "defined": "^1.0.0", - "dotignore": "^0.1.2", - "for-each": "^0.3.3", - "get-package-type": "^0.1.0", - "glob": "^7.2.0", - "has": "^1.0.3", - "has-dynamic-import": "^2.0.1", - "inherits": "^2.0.4", - "is-regex": "^1.1.4", - "minimist": "^1.2.5", - "object-inspect": "^1.12.0", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "resolve": "^2.0.0-next.3", - "resumer": "^0.0.0", - "string.prototype.trim": "^1.2.5", - "through": "^2.3.8" + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/tap/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/js-tokens": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/jsesc": { + "version": "2.5.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tap/node_modules/json5": { + "version": "2.2.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tap/node_modules/locate-path": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/lodash": { + "version": "4.17.21", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/loose-envify": { + "version": "1.4.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { - "tape": "bin/tape" + "loose-envify": "cli.js" + } + }, + "node_modules/tap/node_modules/make-dir": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tap/node_modules/mimic-fn": { + "version": "2.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tap/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tap/node_modules/minipass": { + "version": "3.3.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/ms": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/node-releases": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/object-assign": { + "version": "4.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tap/node_modules/once": { + "version": "1.4.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/tap/node_modules/onetime": { + "version": "5.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tap/node_modules/p-limit": { + "version": "2.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tap/node_modules/p-locate": { + "version": "4.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/p-try": { + "version": "2.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tap/node_modules/patch-console": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/tap/node_modules/path-exists": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/path-is-absolute": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tap/node_modules/picocolors": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/tap/node_modules/pkg-dir": { + "version": "4.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/punycode": { + "version": "2.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tap/node_modules/react": { + "version": "17.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tap/node_modules/react-devtools-core": { + "version": "4.24.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "shell-quote": "^1.6.1", + "ws": "^7" + } + }, + "node_modules/tap/node_modules/react-reconciler": { + "version": "0.26.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "scheduler": "^0.20.2" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "react": "^17.0.2" + } + }, + "node_modules/tap/node_modules/redeyed": { + "version": "2.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "esprima": "~4.0.0" + } + }, + "node_modules/tap/node_modules/resolve-from": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/tap/node_modules/restore-cursor": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/rimraf": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tap/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/scheduler": { + "version": "0.20.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "node_modules/tap/node_modules/semver": { + "version": "6.3.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/tap/node_modules/shell-quote": { + "version": "1.7.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/signal-exit": { + "version": "3.0.7", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/tap/node_modules/slice-ansi": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/tap/node_modules/slice-ansi/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/tap/node_modules/slice-ansi/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/source-map": { + "version": "0.5.7", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tap/node_modules/stack-utils": { + "version": "2.0.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tap/node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/supports-color": { + "version": "5.5.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tap/node_modules/tap-parser": { + "version": "11.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "events-to-array": "^1.0.1", + "minipass": "^3.1.6", + "tap-yaml": "^1.0.0" + }, + "bin": { + "tap-parser": "bin/cmd.js" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/tap/node_modules/tap-yaml": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yaml": "^1.10.2" + } + }, + "node_modules/tap/node_modules/tcompare": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/tcompare/-/tcompare-5.0.7.tgz", + "integrity": "sha512-d9iddt6YYGgyxJw5bjsN7UJUO1kGOtjSlNy/4PoGYAjQS5pAT/hzIoLf1bZCw+uUxRmZJh7Yy1aA7xKVRT9B4w==", + "dev": true, + "dependencies": { + "diff": "^4.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tap/node_modules/to-fast-properties": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/tap/node_modules/treport": { + "version": "3.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/import-jsx": "^4.0.1", + "cardinal": "^2.1.1", + "chalk": "^3.0.0", + "ink": "^3.2.0", + "ms": "^2.1.2", + "tap-parser": "^11.0.0", + "tap-yaml": "^1.0.0", + "unicode-length": "^2.0.2" + }, + "peerDependencies": { + "react": "^17.0.2" + } + }, + "node_modules/tap/node_modules/treport/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/tap/node_modules/treport/node_modules/chalk": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/treport/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/tap/node_modules/treport/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/treport/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/treport/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/type-fest": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tap/node_modules/unicode-length": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.0.0", + "strip-ansi": "^3.0.1" + } + }, + "node_modules/tap/node_modules/unicode-length/node_modules/ansi-regex": { + "version": "2.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tap/node_modules/unicode-length/node_modules/strip-ansi": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tap/node_modules/widest-line": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/wrap-ansi": { + "version": "6.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tap/node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/tap/node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/tap/node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/tap/node_modules/wrappy": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/tap/node_modules/ws": { + "version": "7.5.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/tap/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/tap/node_modules/yaml": { + "version": "1.10.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/tap/node_modules/yoga-layout-prebuilt": { + "version": "1.10.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@types/yoga-layout": "1.9.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tcompare": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tcompare/-/tcompare-6.0.0.tgz", + "integrity": "sha512-JeX89lSVkxTzYND0LxzFCGrXm/TqGEQ0heu1JTwplnpaYQNky6hIaO4lQBOrs+/P787i3CoK9T/O3/oEcnJXvA==", + "dev": true, + "dependencies": { + "diff": "^5.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tcompare/node_modules/diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=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==", - "dev": true + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } }, "node_modules/to-regex-range": { "version": "5.0.1", @@ -1772,11 +4710,19 @@ "node": ">=8.0" } }, + "node_modules/trivial-deferred": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/trivial-deferred/-/trivial-deferred-1.1.2.tgz", + "integrity": "sha512-vDPiDBC3hyP6O4JrJYMImW3nl3c03Tsj9fEXc7Qc/XKa1O7gf5ZtFfIR/E0dun9SnDHdwjna1Z2rSzYgqpxh/g==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, "dependencies": { "prelude-ls": "^1.2.1" }, @@ -1788,7 +4734,6 @@ "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, "engines": { "node": ">=10" }, @@ -1796,41 +4741,71 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "node_modules/typedarray-to-buffer": { + "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": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", - "which-boxed-primitive": "^1.0.2" + "is-typedarray": "^1.0.0" + } + }, + "node_modules/unicode-length": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-length/-/unicode-length-2.1.0.tgz", + "integrity": "sha512-4bV582zTV9Q02RXBxSUMiuN/KHo5w4aTojuKTNT96DIKps/SIawFp7cS5Mu25VuY1AioGXrmYyzKZUzh8OqoUw==", + "dev": true, + "dependencies": { + "punycode": "^2.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "dependencies": { "punycode": "^2.1.0" } }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } }, "node_modules/which": { "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" }, @@ -1841,83 +4816,476 @@ "node": ">= 8" } }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "node_modules/which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", + "dev": true + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "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": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" } }, - "node_modules/which-typed-array": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz", - "integrity": "sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==", + "node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-abstract": "^1.18.5", - "foreach": "^2.0.5", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.7" + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" }, "engines": { - "node": ">= 0.4" + "node": ">=8" + } + }, + "node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=6" } }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "node_modules/yargs/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/yargs/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } }, "dependencies": { - "@eslint/eslintrc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", - "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", + "@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/compat-data": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", + "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==", + "dev": true + }, + "@babel/core": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.3.tgz", + "integrity": "sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw==", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.21.3", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-module-transforms": "^7.21.2", + "@babel/helpers": "^7.21.0", + "@babel/parser": "^7.21.3", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.3", + "@babel/types": "^7.21.3", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.0" + } + }, + "@babel/generator": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.3.tgz", + "integrity": "sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==", + "dev": true, + "requires": { + "@babel/types": "^7.21.3", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@babel/helper-compilation-targets": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", + "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.20.5", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", + "semver": "^6.3.0" + } + }, + "@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "dev": true + }, + "@babel/helper-function-name": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", + "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "dev": true, + "requires": { + "@babel/template": "^7.20.7", + "@babel/types": "^7.21.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-transforms": { + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", + "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.2", + "@babel/types": "^7.21.2" + } + }, + "@babel/helper-simple-access": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "dev": true, + "requires": { + "@babel/types": "^7.20.2" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", + "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "dev": true + }, + "@babel/helpers": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", + "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", "dev": true, "requires": { + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.0", + "@babel/types": "^7.21.0" + } + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "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" + } + }, + "chalk": { + "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", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "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" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "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==", + "dev": true + }, + "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==", + "dev": true + }, + "supports-color": { + "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" + } + } + } + }, + "@babel/parser": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.3.tgz", + "integrity": "sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==", + "dev": true + }, + "@babel/template": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" + } + }, + "@babel/traverse": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.3.tgz", + "integrity": "sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.21.3", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.21.3", + "@babel/types": "^7.21.3", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "dependencies": { + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.3.tgz", + "integrity": "sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.0.tgz", + "integrity": "sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==" + }, + "@eslint/eslintrc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.2.tgz", + "integrity": "sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==", + "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.3.2", - "globals": "^13.15.0", + "espree": "^9.5.1", + "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -1925,41 +5293,163 @@ "strip-json-comments": "^3.1.1" } }, + "@eslint/js": { + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.37.0.tgz", + "integrity": "sha512-x5vzdtOOGgFVDCUs81QRB2+liax8rFg3+7hqM+QhBG0/G3F1ZsoYl97UrqgHgQ9KKT7G6c4V+aTUCgu/n22v1A==" + }, "@humanwhocodes/config-array": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", - "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", - "dev": true, + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", "requires": { "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", - "minimatch": "^3.0.4" + "minimatch": "^3.0.5" } }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==" + }, "@humanwhocodes/object-schema": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true }, - "acorn": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", - "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", + "@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "dev": true }, + "@jridgewell/trace-mapping": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==" + }, "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, "requires": {} }, + "aggregate-error": { + "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" + } + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -1970,14 +5460,12 @@ "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "requires": { "color-convert": "^2.0.1" } @@ -1992,34 +5480,36 @@ "picomatch": "^2.0.4" } }, + "append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "requires": { + "default-require-extensions": "^3.0.0" + } + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", + "dev": true + }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, - "array.prototype.every": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/array.prototype.every/-/array.prototype.every-1.1.3.tgz", - "integrity": "sha512-vWnriJI//SOMOWtXbU/VXhJ/InfnNHPF6BLKn5WfY8xXy+NWql0fUy20GO3sdqBhCAO+qw8S/E5nJiZX+QFdCA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "is-string": "^1.0.7" - } - }, - "available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "async-hook-domain": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/async-hook-domain/-/async-hook-domain-2.0.4.tgz", + "integrity": "sha512-14LjCmlK1PK8eDtTezR6WX8TMaYNIzBIsd2D1sGoGjgx0BuNMMoSdk7i/drlbtamy0AWv9yv2tkB+ASdmeqFIw==", "dev": true }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "binary-extensions": { "version": "2.2.0", @@ -2027,11 +5517,16 @@ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, + "bind-obj-methods": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bind-obj-methods/-/bind-obj-methods-3.0.0.tgz", + "integrity": "sha512-nLEaaz3/sEzNSyPWRsN9HNsqwk1AUyECtGj+XwGdIi3xABnEqecvXtIJ0wehQXuuER5uZ/5fTs2usONgYjG+iw==", + "dev": true + }, "brace-expansion": { "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" @@ -2046,27 +5541,57 @@ "fill-range": "^7.0.1" } }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "browserslist": { + "version": "4.21.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", + "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" } }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30001469", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001469.tgz", + "integrity": "sha512-Rcp7221ScNqQPP3W+lVOYDyjdR6dC+neEQCttoNr5bAyz54AboB4iwpnWgyi8P4YUsPybVzT4LgWiBbI3drL4g==", "dev": true }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2104,11 +5629,27 @@ "resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-2.4.2.tgz", "integrity": "sha512-U9eDw6+wt7V8z5NncY2jJfZa+hUH8XEj8FQHgFJTrUFnJfXYf4Ml4adI2vXZOjqRDpFWtYVWypDfZwnJ+HIR4A==" }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "requires": { "color-name": "~1.1.4" } @@ -2116,7 +5657,12 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", "dev": true }, "command-exists": { @@ -2124,17 +5670,27 @@ "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==" }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "dev": true }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -2145,142 +5701,85 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "requires": { "ms": "2.1.2" } }, - "deep-equal": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.5.tgz", - "integrity": "sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "es-get-iterator": "^1.1.1", - "get-intrinsic": "^1.0.1", - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.2", - "is-regex": "^1.1.1", - "isarray": "^2.0.5", - "object-is": "^1.1.4", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "regexp.prototype.flags": "^1.3.0", - "side-channel": "^1.0.3", - "which-boxed-primitive": "^1.0.1", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.2" - } + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true }, "deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "default-require-extensions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", + "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", "dev": true, "requires": { - "object-keys": "^1.0.12" + "strip-bom": "^4.0.0" } }, - "defined": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, "requires": { "esutils": "^2.0.2" } }, - "dotignore": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/dotignore/-/dotignore-0.1.2.tgz", - "integrity": "sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw==", - "dev": true, - "requires": { - "minimatch": "^3.0.4" - } + "electron-to-chromium": { + "version": "1.4.340", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.340.tgz", + "integrity": "sha512-zx8hqumOqltKsv/MF50yvdAlPF9S/4PXbyfzJS6ZGhbddGkRegdwImmfSVqCkEziYzrIGZ/TlrzBND4FysfkDg==", + "dev": true }, - "es-abstract": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", - "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.1", - "is-string": "^1.0.7", - "is-weakref": "^1.0.1", - "object-inspect": "^1.11.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" - } - }, - "es-get-iterator": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.2.tgz", - "integrity": "sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.0", - "has-symbols": "^1.0.1", - "is-arguments": "^1.1.0", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.5", - "isarray": "^2.0.5" - } + "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==", + "dev": true }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" }, "eslint": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.18.0.tgz", - "integrity": "sha512-As1EfFMVk7Xc6/CvhssHUjsAQSkpfXvUGMFC3ce8JDe6WvqCgRrLOBQbVpsBFr1X1V+RACOadnzVvcUS5ni2bA==", - "dev": true, + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.37.0.tgz", + "integrity": "sha512-NU3Ps9nI05GUoVMxcZx1J8CNR6xOvUT4jAUMH5+z8lpp3aEdPVCImKw6PWG4PY+Vfkpr+jvMpxs/qoE7wq0sPw==", "requires": { - "@eslint/eslintrc": "^1.3.0", - "@humanwhocodes/config-array": "^0.9.2", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.2", + "@eslint/js": "8.37.0", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -2288,20 +5787,22 @@ "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.2", - "esquery": "^1.4.0", + "eslint-visitor-keys": "^3.4.0", + "espree": "^9.5.1", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.15.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", @@ -2309,62 +5810,80 @@ "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "regexpp": "^3.2.0", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "text-table": "^0.2.0" + }, + "dependencies": { + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "requires": { + "p-locate": "^5.0.0" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "requires": { + "p-limit": "^3.0.2" + } + } } }, "eslint-scope": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, "requires": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - } - } - }, "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz", + "integrity": "sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==" }, "espree": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", - "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", - "dev": true, + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.1.tgz", + "integrity": "sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==", "requires": { - "acorn": "^8.7.1", + "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.0" } }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "requires": { "estraverse": "^5.1.0" } @@ -2373,7 +5892,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, "requires": { "estraverse": "^5.2.0" } @@ -2381,38 +5899,46 @@ "estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "events-to-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/events-to-array/-/events-to-array-1.1.2.tgz", + "integrity": "sha512-inRWzRY7nG+aXZxBzEqYKB3HPgwflZRopAjDCHv0whhRx+MTUr1ei0ICZUypdyE0HRm4L2d5VEcIqLD6yl+BFA==", "dev": true }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + }, + "fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "requires": { + "reusify": "^1.0.4" + } }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, "requires": { "flat-cache": "^3.0.4" } @@ -2426,11 +5952,37 @@ "to-regex-range": "^5.0.1" } }, + "find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "findit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findit/-/findit-2.0.0.tgz", + "integrity": "sha512-ENZS237/Hr8bjczn5eKuBohLgaD0JyUd0arxretR1f9RO46vZHA1b2y0VorgGV3WaOT3c+78P8h7v4JGJ1i/rg==", + "dev": true + }, "flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, "requires": { "flatted": "^3.1.0", "rimraf": "^3.0.2" @@ -2439,29 +5991,34 @@ "flatted": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", - "dev": true + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==" }, - "for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", "dev": true, "requires": { - "is-callable": "^1.1.3" + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" } }, - "foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true + }, + "fs-exists-cached": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-exists-cached/-/fs-exists-cached-1.0.0.tgz", + "integrity": "sha512-kSxoARUDn4F2RPXX48UXnaFKwVU7Ivd/6qpzZL29MCDmr9sTvybv4gFCp+qaI4fM9m0z9fgz/yJvi56GAz+BZg==", "dev": true }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { "version": "2.3.2", @@ -2470,28 +6027,23 @@ "dev": true, "optional": true }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "function-loop": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/function-loop/-/function-loop-2.0.1.tgz", + "integrity": "sha512-ktIR+O6i/4h+j/ZhZJNdzeI4i9lEPeEK6UPR2EVyTVBqOwcU3Za9xYKLH64ZR9HmcROyRrOkizNyjjtWJzDDkQ==", "dev": true }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true }, - "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - } + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true }, "get-package-type": { "version": "0.1.0", @@ -2499,26 +6051,15 @@ "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true }, - "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } - }, "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } @@ -2527,64 +6068,50 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, "requires": { "is-glob": "^4.0.3" } }, "globals": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", - "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", - "dev": true, + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "requires": { "type-fest": "^0.20.2" } }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "has-dynamic-import": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-dynamic-import/-/has-dynamic-import-2.0.1.tgz", - "integrity": "sha512-X3fbtsZmwb6W7fJGR9o7x65fZoodygCrZ3TVycvghP62yYQfS0t4RS0Qcz+j5tQYUKeSWS09tHkWW6WhFV3XhQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", "dev": true, "requires": { - "has-symbols": "^1.0.2" + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } } }, "he": { @@ -2592,17 +6119,21 @@ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" }, - "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, + "ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==" + }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -2611,14 +6142,18 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==" + }, + "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==", "dev": true }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -2627,38 +6162,7 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - } - }, - "is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "requires": { - "has-bigints": "^1.0.1" - } + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "is-binary-path": { "version": "2.1.0", @@ -2669,170 +6173,150 @@ "binary-extensions": "^2.0.0" } }, - "is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", - "dev": true - }, - "is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + }, + "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==", "dev": true }, "is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "requires": { "is-extglob": "^2.1.1" } }, - "is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", - "dev": true - }, - "is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true - }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, - "is-number-object": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", - "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==" + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true + }, + "istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", "dev": true, "requires": { - "has-tostringtag": "^1.0.0" + "append-transform": "^2.0.0" } }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" } }, - "is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", - "dev": true - }, - "is-shared-array-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", - "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", - "dev": true - }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "istanbul-lib-processinfo": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", + "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", "dev": true, "requires": { - "has-tostringtag": "^1.0.0" + "archy": "^1.0.0", + "cross-spawn": "^7.0.3", + "istanbul-lib-coverage": "^3.2.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^8.3.2" } }, - "is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", "dev": true, "requires": { - "has-symbols": "^1.0.2" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" } }, - "is-typed-array": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz", - "integrity": "sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==", + "istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-abstract": "^1.18.5", - "foreach": "^2.0.5", - "has-tostringtag": "^1.0.0" + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" } }, - "is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", - "dev": true - }, - "is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", "dev": true, "requires": { - "call-bind": "^1.0.2" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" } }, - "is-weakset": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "jackspeak": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-1.4.2.tgz", + "integrity": "sha512-GHeGTmnuaHnvS+ZctRB01bfxARuu9wW83ENbuiweu07SFcVlZrJpcshSre/keGT7YGBhLHg/+rXCNSrsEHKU4Q==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "cliui": "^7.0.4" } }, - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true + "js-sdsl": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz", + "integrity": "sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==" }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, "js-yaml": { @@ -2843,59 +6327,166 @@ "argparse": "^2.0.1" } }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" + }, + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, "requires": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, + "libtap": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/libtap/-/libtap-1.4.0.tgz", + "integrity": "sha512-STLFynswQ2A6W14JkabgGetBNk6INL1REgJ9UeNKw5llXroC2cGLgKTqavv0sl8OLVztLLipVKMcQ7yeUcqpmg==", + "dev": true, + "requires": { + "async-hook-domain": "^2.0.4", + "bind-obj-methods": "^3.0.0", + "diff": "^4.0.2", + "function-loop": "^2.0.1", + "minipass": "^3.1.5", + "own-or": "^1.0.0", + "own-or-env": "^1.0.2", + "signal-exit": "^3.0.4", + "stack-utils": "^2.0.4", + "tap-parser": "^11.0.0", + "tap-yaml": "^1.0.0", + "tcompare": "^5.0.6", + "trivial-deferred": "^1.0.1" + }, + "dependencies": { + "tcompare": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/tcompare/-/tcompare-5.0.7.tgz", + "integrity": "sha512-d9iddt6YYGgyxJw5bjsN7UJUO1kGOtjSlNy/4PoGYAjQS5pAT/hzIoLf1bZCw+uUxRmZJh7Yy1aA7xKVRT9B4w==", + "dev": true, + "requires": { + "diff": "^4.0.2" + } + } + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", + "dev": true + }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + }, + "dependencies": { + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "marked": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-5.0.2.tgz", + "integrity": "sha512-TXksm9GwqXCRNbFUZmMtqNLvy3K2cQHuWmyBDLOrY1e6i9UvZpOTJXoz7fBjYkJkaUFzV9hBFxMuZSyQt8R6KQ==" }, "minimatch": { "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" } }, - "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + }, + "node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "requires": { + "process-on-spawn": "^1.0.0" + } + }, + "node-releases": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", + "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", "dev": true }, "normalize-path": { @@ -2904,54 +6495,67 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, - "object-inspect": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", - "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", - "dev": true - }, - "object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "nyc": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", + "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } } }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } }, + "opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true + }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, "requires": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -2961,31 +6565,93 @@ "word-wrap": "^1.2.3" } }, + "own-or": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/own-or/-/own-or-1.0.0.tgz", + "integrity": "sha512-NfZr5+Tdf6MB8UI9GLvKRs4cXY8/yB0w3xtt84xFdWy8hkGjn+JFc60VhzS/hFRfbyxFcGYMTjnF4Me+RbbqrA==", + "dev": true + }, + "own-or-env": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/own-or-env/-/own-or-env-1.0.2.tgz", + "integrity": "sha512-NQ7v0fliWtK7Lkb+WdFqe6ky9XAzYmlkXthQrBbzlYbmFKoAYbDDcwmOm6q8kOuwSRXW8bdL5ORksploUJmWgw==", + "dev": true, + "requires": { + "own-or": "^1.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + } + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "requires": { "callsites": "^3.0.0" } }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, "picomatch": { @@ -2994,17 +6660,38 @@ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" + }, + "process-on-spawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", + "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "dev": true, + "requires": { + "fromentries": "^1.2.0" + } }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" }, "readdirp": { "version": "3.6.0", @@ -3015,61 +6702,69 @@ "picomatch": "^2.2.1" } }, - "regexp.prototype.flags": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz", - "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==", + "release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "es6-error": "^4.0.1" } }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, - "resolve": { - "version": "2.0.0-next.3", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", - "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", - "dev": true, - "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - } + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" }, - "resumer": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", - "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=", - "dev": true, - "requires": { - "through": "~2.3.4" - } + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" }, "rimraf": { "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" } }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "requires": { "shebang-regex": "^3.0.0" } @@ -3077,114 +6772,1453 @@ "shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "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==", "dev": true }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "string.prototype.trim": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.5.tgz", - "integrity": "sha512-Lnh17webJVsD6ECeovpVN17RlAKjmz4rF9S+8Y45CkMc/ufVpTkU3vZIyIC7sllQ1FCvObZnnCdNs/HXTUOTlg==", + "spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" } }, - "string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + } } }, - "string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "string-width": { + "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": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" } }, "strip-ansi": { "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" } }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "requires": { "has-flag": "^4.0.0" } }, - "tape": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/tape/-/tape-5.4.1.tgz", - "integrity": "sha512-7bGaJ3WnQ/CX3xOWzlR+9lNptEWoD+11gyREP8k+SYrDu2a20EifKpTmZndXn25ZRxesYHSuNtE7Fb+THcjfGA==", + "tap": { + "version": "16.3.4", + "resolved": "https://registry.npmjs.org/tap/-/tap-16.3.4.tgz", + "integrity": "sha512-SAexdt2ZF4XBgye6TPucFI2y7VE0qeFXlXucJIV1XDPCs+iJodk0MYacr1zR6Ycltzz7PYg8zrblDXKbAZM2LQ==", "dev": true, "requires": { - "array.prototype.every": "^1.1.3", - "call-bind": "^1.0.2", - "deep-equal": "^2.0.5", - "defined": "^1.0.0", - "dotignore": "^0.1.2", - "for-each": "^0.3.3", - "get-package-type": "^0.1.0", - "glob": "^7.2.0", - "has": "^1.0.3", - "has-dynamic-import": "^2.0.1", - "inherits": "^2.0.4", - "is-regex": "^1.1.4", - "minimist": "^1.2.5", - "object-inspect": "^1.12.0", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "resolve": "^2.0.0-next.3", - "resumer": "^0.0.0", - "string.prototype.trim": "^1.2.5", - "through": "^2.3.8" + "@isaacs/import-jsx": "^4.0.1", + "@types/react": "^17.0.52", + "chokidar": "^3.3.0", + "findit": "^2.0.0", + "foreground-child": "^2.0.0", + "fs-exists-cached": "^1.0.0", + "glob": "^7.2.3", + "ink": "^3.2.0", + "isexe": "^2.0.0", + "istanbul-lib-processinfo": "^2.0.3", + "jackspeak": "^1.4.2", + "libtap": "^1.4.0", + "minipass": "^3.3.4", + "mkdirp": "^1.0.4", + "nyc": "^15.1.0", + "opener": "^1.5.1", + "react": "^17.0.2", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.6", + "source-map-support": "^0.5.16", + "tap-mocha-reporter": "^5.0.3", + "tap-parser": "^11.0.2", + "tap-yaml": "^1.0.2", + "tcompare": "^5.0.7", + "treport": "^3.0.4", + "which": "^2.0.2" + }, + "dependencies": { + "@ampproject/remapping": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.0" + } + }, + "@babel/code-frame": { + "version": "7.16.7", + "bundled": true, + "dev": true, + "requires": { + "@babel/highlight": "^7.16.7" + } + }, + "@babel/compat-data": { + "version": "7.17.7", + "bundled": true, + "dev": true + }, + "@babel/core": { + "version": "7.17.8", + "bundled": true, + "dev": true, + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.7", + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helpers": "^7.17.8", + "@babel/parser": "^7.17.8", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0" + } + }, + "@babel/generator": { + "version": "7.17.7", + "bundled": true, + "dev": true, + "requires": { + "@babel/types": "^7.17.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "bundled": true, + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.17.7", + "bundled": true, + "dev": true, + "requires": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-validator-option": "^7.16.7", + "browserslist": "^4.17.5", + "semver": "^6.3.0" + } + }, + "@babel/helper-environment-visitor": { + "version": "7.16.7", + "bundled": true, + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-function-name": { + "version": "7.16.7", + "bundled": true, + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.16.7", + "bundled": true, + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.16.7", + "bundled": true, + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-module-imports": { + "version": "7.16.7", + "bundled": true, + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-module-transforms": { + "version": "7.17.7", + "bundled": true, + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.16.7", + "bundled": true, + "dev": true + }, + "@babel/helper-simple-access": { + "version": "7.17.7", + "bundled": true, + "dev": true, + "requires": { + "@babel/types": "^7.17.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.16.7", + "bundled": true, + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.16.7", + "bundled": true, + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.16.7", + "bundled": true, + "dev": true + }, + "@babel/helpers": { + "version": "7.17.8", + "bundled": true, + "dev": true, + "requires": { + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" + } + }, + "@babel/highlight": { + "version": "7.16.10", + "bundled": true, + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.17.8", + "bundled": true, + "dev": true + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.17.3", + "bundled": true, + "dev": true, + "requires": { + "@babel/compat-data": "^7.17.0", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.16.7" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.16.7", + "bundled": true, + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "bundled": true, + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.17.7", + "bundled": true, + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.16.7", + "bundled": true, + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-react-jsx": { + "version": "7.17.3", + "bundled": true, + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-jsx": "^7.16.7", + "@babel/types": "^7.17.0" + } + }, + "@babel/template": { + "version": "7.16.7", + "bundled": true, + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + } + }, + "@babel/traverse": { + "version": "7.17.3", + "bundled": true, + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.3", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/parser": "^7.17.3", + "@babel/types": "^7.17.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.17.0", + "bundled": true, + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.16.7", + "to-fast-properties": "^2.0.0" + } + }, + "@isaacs/import-jsx": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "requires": { + "@babel/core": "^7.5.5", + "@babel/plugin-proposal-object-rest-spread": "^7.5.5", + "@babel/plugin-transform-destructuring": "^7.5.0", + "@babel/plugin-transform-react-jsx": "^7.3.0", + "caller-path": "^3.0.1", + "find-cache-dir": "^3.2.0", + "make-dir": "^3.0.2", + "resolve-from": "^3.0.0", + "rimraf": "^3.0.0" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.0.5", + "bundled": true, + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.11", + "bundled": true, + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.4", + "bundled": true, + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@types/prop-types": { + "version": "15.7.4", + "bundled": true, + "dev": true + }, + "@types/react": { + "version": "17.0.52", + "bundled": true, + "dev": true, + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/scheduler": { + "version": "0.16.2", + "bundled": true, + "dev": true + }, + "@types/yoga-layout": { + "version": "1.9.2", + "bundled": true, + "dev": true + }, + "ansi-escapes": { + "version": "4.3.2", + "bundled": true, + "dev": true, + "requires": { + "type-fest": "^0.21.3" + }, + "dependencies": { + "type-fest": { + "version": "0.21.3", + "bundled": true, + "dev": true + } + } + }, + "ansi-regex": { + "version": "5.0.1", + "bundled": true, + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "bundled": true, + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "ansicolors": { + "version": "0.3.2", + "bundled": true, + "dev": true + }, + "astral-regex": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "auto-bind": { + "version": "4.0.0", + "bundled": true, + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "browserslist": { + "version": "4.20.2", + "bundled": true, + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001317", + "electron-to-chromium": "^1.4.84", + "escalade": "^3.1.1", + "node-releases": "^2.0.2", + "picocolors": "^1.0.0" + } + }, + "caller-callsite": { + "version": "4.1.0", + "bundled": true, + "dev": true, + "requires": { + "callsites": "^3.1.0" + } + }, + "caller-path": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "caller-callsite": "^4.1.0" + } + }, + "callsites": { + "version": "3.1.0", + "bundled": true, + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30001319", + "bundled": true, + "dev": true + }, + "cardinal": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "requires": { + "ansicolors": "~0.3.2", + "redeyed": "~2.1.0" + } + }, + "chalk": { + "version": "2.4.2", + "bundled": true, + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "ci-info": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "cli-boxes": { + "version": "2.2.1", + "bundled": true, + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "bundled": true, + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-truncate": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + } + }, + "code-excerpt": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "convert-to-spaces": "^1.0.1" + } + }, + "color-convert": { + "version": "1.9.3", + "bundled": true, + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "bundled": true, + "dev": true + }, + "commondir": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "convert-source-map": { + "version": "1.8.0", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "convert-to-spaces": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "csstype": { + "version": "3.0.11", + "bundled": true, + "dev": true + }, + "debug": { + "version": "4.3.4", + "bundled": true, + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "electron-to-chromium": { + "version": "1.4.89", + "bundled": true, + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "bundled": true, + "dev": true + }, + "escalade": { + "version": "3.1.1", + "bundled": true, + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "bundled": true, + "dev": true + }, + "esprima": { + "version": "4.0.1", + "bundled": true, + "dev": true + }, + "events-to-array": { + "version": "1.1.2", + "bundled": true, + "dev": true + }, + "find-cache-dir": { + "version": "3.3.2", + "bundled": true, + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "bundled": true, + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "gensync": { + "version": "1.0.0-beta.2", + "bundled": true, + "dev": true + }, + "glob": { + "version": "7.2.3", + "bundled": true, + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "globals": { + "version": "11.12.0", + "bundled": true, + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "bundled": true, + "dev": true + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "bundled": true, + "dev": true + }, + "ink": { + "version": "3.2.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "auto-bind": "4.0.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.0", + "cli-cursor": "^3.1.0", + "cli-truncate": "^2.1.0", + "code-excerpt": "^3.0.0", + "indent-string": "^4.0.0", + "is-ci": "^2.0.0", + "lodash": "^4.17.20", + "patch-console": "^1.0.0", + "react-devtools-core": "^4.19.1", + "react-reconciler": "^0.26.2", + "scheduler": "^0.20.2", + "signal-exit": "^3.0.2", + "slice-ansi": "^3.0.0", + "stack-utils": "^2.0.2", + "string-width": "^4.2.2", + "type-fest": "^0.12.0", + "widest-line": "^3.1.0", + "wrap-ansi": "^6.2.0", + "ws": "^7.5.5", + "yoga-layout-prebuilt": "^1.9.6" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "bundled": true, + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "bundled": true, + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "bundled": true, + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "bundled": true, + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "is-ci": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "bundled": true, + "dev": true + }, + "jsesc": { + "version": "2.5.2", + "bundled": true, + "dev": true + }, + "json5": { + "version": "2.2.3", + "bundled": true, + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "bundled": true, + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.21", + "bundled": true, + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "make-dir": { + "version": "3.1.0", + "bundled": true, + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "bundled": true, + "dev": true + }, + "minimatch": { + "version": "3.1.2", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minipass": { + "version": "3.3.4", + "bundled": true, + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "ms": { + "version": "2.1.2", + "bundled": true, + "dev": true + }, + "node-releases": { + "version": "2.0.2", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "bundled": true, + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "bundled": true, + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "bundled": true, + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "bundled": true, + "dev": true + }, + "patch-console": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "bundled": true, + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "bundled": true, + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "punycode": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "react": { + "version": "17.0.2", + "bundled": true, + "dev": true, + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "react-devtools-core": { + "version": "4.24.1", + "bundled": true, + "dev": true, + "requires": { + "shell-quote": "^1.6.1", + "ws": "^7" + } + }, + "react-reconciler": { + "version": "0.26.2", + "bundled": true, + "dev": true, + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "scheduler": "^0.20.2" + } + }, + "redeyed": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "requires": { + "esprima": "~4.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "restore-cursor": { + "version": "3.1.0", + "bundled": true, + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "rimraf": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "dev": true + }, + "scheduler": { + "version": "0.20.2", + "bundled": true, + "dev": true, + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "semver": { + "version": "6.3.0", + "bundled": true, + "dev": true + }, + "shell-quote": { + "version": "1.7.3", + "bundled": true, + "dev": true + }, + "signal-exit": { + "version": "3.0.7", + "bundled": true, + "dev": true + }, + "slice-ansi": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "bundled": true, + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "bundled": true, + "dev": true + } + } + }, + "source-map": { + "version": "0.5.7", + "bundled": true, + "dev": true + }, + "stack-utils": { + "version": "2.0.5", + "bundled": true, + "dev": true, + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "bundled": true, + "dev": true + } + } + }, + "string-width": { + "version": "4.2.3", + "bundled": true, + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "supports-color": { + "version": "5.5.0", + "bundled": true, + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "tap-parser": { + "version": "11.0.2", + "bundled": true, + "dev": true, + "requires": { + "events-to-array": "^1.0.1", + "minipass": "^3.1.6", + "tap-yaml": "^1.0.0" + } + }, + "tap-yaml": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "yaml": "^1.10.2" + } + }, + "tcompare": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/tcompare/-/tcompare-5.0.7.tgz", + "integrity": "sha512-d9iddt6YYGgyxJw5bjsN7UJUO1kGOtjSlNy/4PoGYAjQS5pAT/hzIoLf1bZCw+uUxRmZJh7Yy1aA7xKVRT9B4w==", + "dev": true, + "requires": { + "diff": "^4.0.2" + } + }, + "to-fast-properties": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "treport": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "@isaacs/import-jsx": "^4.0.1", + "cardinal": "^2.1.1", + "chalk": "^3.0.0", + "ink": "^3.2.0", + "ms": "^2.1.2", + "tap-parser": "^11.0.0", + "tap-yaml": "^1.0.0", + "unicode-length": "^2.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "bundled": true, + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "bundled": true, + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "bundled": true, + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "bundled": true, + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "type-fest": { + "version": "0.12.0", + "bundled": true, + "dev": true + }, + "unicode-length": { + "version": "2.0.2", + "bundled": true, + "dev": true, + "requires": { + "punycode": "^2.0.0", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "widest-line": { + "version": "3.1.0", + "bundled": true, + "dev": true, + "requires": { + "string-width": "^4.0.0" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "bundled": true, + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "bundled": true, + "dev": true + } + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "ws": { + "version": "7.5.7", + "bundled": true, + "dev": true, + "requires": {} + }, + "yallist": { + "version": "4.0.0", + "bundled": true, + "dev": true + }, + "yaml": { + "version": "1.10.2", + "bundled": true, + "dev": true + }, + "yoga-layout-prebuilt": { + "version": "1.10.0", + "bundled": true, + "dev": true, + "requires": { + "@types/yoga-layout": "1.9.2" + } + } + } + }, + "tap-mocha-reporter": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/tap-mocha-reporter/-/tap-mocha-reporter-5.0.3.tgz", + "integrity": "sha512-6zlGkaV4J+XMRFkN0X+yuw6xHbE9jyCZ3WUKfw4KxMyRGOpYSRuuQTRJyWX88WWuLdVTuFbxzwXhXuS2XE6o0g==", + "dev": true, + "requires": { + "color-support": "^1.1.0", + "debug": "^4.1.1", + "diff": "^4.0.1", + "escape-string-regexp": "^2.0.0", + "glob": "^7.0.5", + "tap-parser": "^11.0.0", + "tap-yaml": "^1.0.0", + "unicode-length": "^2.0.2" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + } + } + }, + "tap-parser": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-11.0.2.tgz", + "integrity": "sha512-6qGlC956rcORw+fg7Fv1iCRAY8/bU9UabUAhs3mXRH6eRmVZcNPLheSXCYaVaYeSwx5xa/1HXZb1537YSvwDZg==", + "dev": true, + "requires": { + "events-to-array": "^1.0.1", + "minipass": "^3.1.6", + "tap-yaml": "^1.0.0" + } + }, + "tap-yaml": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tap-yaml/-/tap-yaml-1.0.2.tgz", + "integrity": "sha512-GegASpuqBnRNdT1U+yuUPZ8rEU64pL35WPBpCISWwff4dErS2/438barz7WFJl4Nzh3Y05tfPidZnH+GaV1wMg==", + "dev": true, + "requires": { + "yaml": "^1.10.2" + } + }, + "tcompare": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tcompare/-/tcompare-6.0.0.tgz", + "integrity": "sha512-JeX89lSVkxTzYND0LxzFCGrXm/TqGEQ0heu1JTwplnpaYQNky6hIaO4lQBOrs+/P787i3CoK9T/O3/oEcnJXvA==", + "dev": true, + "requires": { + "diff": "^5.1.0" + }, + "dependencies": { + "diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "dev": true + } + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" } }, "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==", - "dev": true + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "dev": true }, "to-regex-range": { @@ -3196,11 +8230,16 @@ "is-number": "^7.0.0" } }, + "trivial-deferred": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/trivial-deferred/-/trivial-deferred-1.1.2.tgz", + "integrity": "sha512-vDPiDBC3hyP6O4JrJYMImW3nl3c03Tsj9fEXc7Qc/XKa1O7gf5ZtFfIR/E0dun9SnDHdwjna1Z2rSzYgqpxh/g==", + "dev": true + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, "requires": { "prelude-ls": "^1.2.1" } @@ -3208,94 +8247,172 @@ "type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" }, - "unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "typedarray-to-buffer": { + "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" + } + }, + "unicode-length": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-length/-/unicode-length-2.1.0.tgz", + "integrity": "sha512-4bV582zTV9Q02RXBxSUMiuN/KHo5w4aTojuKTNT96DIKps/SIawFp7cS5Mu25VuY1AioGXrmYyzKZUzh8OqoUw==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", - "which-boxed-primitive": "^1.0.2" + "punycode": "^2.0.0" + } + }, + "update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "dev": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" } }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "requires": { "punycode": "^2.1.0" } }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true }, "which": { "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" } }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", + "dev": true + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" } }, - "which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "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": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" } }, - "which-typed-array": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz", - "integrity": "sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==", + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-abstract": "^1.18.5", - "foreach": "^2.0.5", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.7" + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "dependencies": { + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + } } }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" } } } diff --git a/package.json b/package.json index 348ca896..67e00fdf 100644 --- a/package.json +++ b/package.json @@ -8,20 +8,26 @@ "hsmusic": "./src/upd8.js" }, "scripts": { - "test": "tape test/**/*.js", + "test": "tap 'test/snapshot/*.js' 'test/unit/**/*.js'", "dev": "eslint src && node src/upd8.js" }, "dependencies": { "chroma-js": "^2.4.2", "command-exists": "^1.2.9", + "eslint": "^8.37.0", "he": "^1.2.0", "js-yaml": "^4.1.0", + "marked": "^5.0.2", "word-wrap": "^1.2.3" }, "license": "GPL-3.0", "devDependencies": { "chokidar": "^3.5.3", - "eslint": "^8.18.0", - "tape": "^5.4.1" + "tap": "^16.3.4", + "tcompare": "^6.0.0" + }, + "tap": { + "coverage": false, + "coverage-report": false } } diff --git a/src/content-function.js b/src/content-function.js new file mode 100644 index 00000000..3536d5a8 --- /dev/null +++ b/src/content-function.js @@ -0,0 +1,478 @@ +import {annotateFunction, empty} from './util/sugar.js'; + +export default function contentFunction({ + contentDependencies = [], + extraDependencies = [], + + sprawl, + relations, + data, + generate, +}) { + return expectDependencies({ + sprawl, + relations, + data, + generate, + + expectedContentDependencyKeys: contentDependencies, + expectedExtraDependencyKeys: extraDependencies, + fulfilledDependencies: {}, + }); +} + +contentFunction.identifyingSymbol = Symbol(`Is a content function?`); + +export function expectDependencies({ + sprawl, + relations, + data, + generate, + + expectedContentDependencyKeys, + expectedExtraDependencyKeys, + fulfilledDependencies, +}) { + if (!generate) { + throw new Error(`Expected generate function`); + } + + const hasSprawlFunction = !!sprawl; + const hasRelationsFunction = !!relations; + const hasDataFunction = !!data; + + if (hasSprawlFunction && !expectedExtraDependencyKeys.includes('wikiData')) { + throw new Error(`Content functions which sprawl must specify wikiData in extraDependencies`); + } + + const fulfilledDependencyKeys = Object.keys(fulfilledDependencies); + + const invalidatingDependencyKeys = Object.entries(fulfilledDependencies) + .filter(([key, value]) => value?.fulfilled === false) + .map(([key]) => key); + + const missingContentDependencyKeys = expectedContentDependencyKeys + .filter(key => !fulfilledDependencyKeys.includes(key)); + + const missingExtraDependencyKeys = expectedExtraDependencyKeys + .filter(key => !fulfilledDependencyKeys.includes(key)); + + let wrappedGenerate; + + if (!empty(invalidatingDependencyKeys)) { + wrappedGenerate = function() { + throw new Error(`Generate invalidated because unfulfilled dependencies provided: ${invalidatingDependencyKeys.join(', ')}`); + }; + + annotateFunction(wrappedGenerate, {name: generate, trait: 'invalidated'}); + wrappedGenerate.fulfilled = false; + } else if (empty(missingContentDependencyKeys) && empty(missingExtraDependencyKeys)) { + wrappedGenerate = function(arg1, arg2) { + if (hasDataFunction && !arg1) { + throw new Error(`Expected data`); + } + + if (hasDataFunction && hasRelationsFunction && !arg2) { + throw new Error(`Expected relations`); + } + + if (hasRelationsFunction && !arg1) { + throw new Error(`Expected relations`); + } + + if (hasDataFunction && hasRelationsFunction) { + return generate(arg1, arg2, fulfilledDependencies); + } else if (hasDataFunction || hasRelationsFunction) { + return generate(arg1, fulfilledDependencies); + } else { + return generate(fulfilledDependencies); + } + }; + + annotateFunction(wrappedGenerate, {name: generate, trait: 'fulfilled'}); + wrappedGenerate.fulfilled = true; + + wrappedGenerate.fulfill = function() { + throw new Error(`All dependencies already fulfilled`); + }; + } else { + wrappedGenerate = function() { + throw new Error(`Dependencies still needed: ${missingContentDependencyKeys.concat(missingExtraDependencyKeys).join(', ')}`); + }; + + annotateFunction(wrappedGenerate, {name: generate, trait: 'unfulfilled'}); + wrappedGenerate.fulfilled = false; + } + + wrappedGenerate[contentFunction.identifyingSymbol] = true; + + if (hasSprawlFunction) { + wrappedGenerate.sprawl = sprawl; + } + + if (hasRelationsFunction) { + wrappedGenerate.relations = relations; + } + + if (hasDataFunction) { + wrappedGenerate.data = data; + } + + wrappedGenerate.fulfill ??= function fulfill(dependencies) { + return expectDependencies({ + sprawl, + relations, + data, + generate, + + expectedContentDependencyKeys, + expectedExtraDependencyKeys, + + fulfilledDependencies: fulfillDependencies({ + name: generate.name, + dependencies, + + expectedContentDependencyKeys, + expectedExtraDependencyKeys, + fulfilledDependencies, + }), + }); + }; + + Object.assign(wrappedGenerate, { + contentDependencies: expectedContentDependencyKeys, + extraDependencies: expectedExtraDependencyKeys, + }); + + return wrappedGenerate; +} + +export function fulfillDependencies({ + name, + dependencies, + expectedContentDependencyKeys, + expectedExtraDependencyKeys, + fulfilledDependencies, +}) { + const newFulfilledDependencies = {...fulfilledDependencies}; + const fulfilledDependencyKeys = Object.keys(fulfilledDependencies); + + const errors = []; + let bail = false; + + for (let [key, value] of Object.entries(dependencies)) { + if (fulfilledDependencyKeys.includes(key)) { + errors.push(new Error(`Dependency ${key} is already fulfilled`)); + bail = true; + continue; + } + + const isContentKey = expectedContentDependencyKeys.includes(key); + const isExtraKey = expectedExtraDependencyKeys.includes(key); + + if (!isContentKey && !isExtraKey) { + errors.push(new Error(`Dependency ${key} is not expected`)); + bail = true; + continue; + } + + if (value === undefined) { + errors.push(new Error(`Dependency ${key} was provided undefined`)); + bail = true; + continue; + } + + if (isContentKey && !value?.[contentFunction.identifyingSymbol]) { + errors.push(new Error(`Content dependency ${key} is not a content function (got ${value})`)); + bail = true; + continue; + } + + if (isExtraKey && value?.[contentFunction.identifyingSymbol]) { + errors.push(new Error(`Extra dependency ${key} is a content function`)); + bail = true; + continue; + } + + if (!bail) { + newFulfilledDependencies[key] = value; + } + } + + if (!empty(errors)) { + throw new AggregateError(errors, `Errors fulfilling dependencies for ${name}`); + } + + return newFulfilledDependencies; +} + +export function getRelationsTree(dependencies, contentFunctionName, wikiData, ...args) { + const relationIdentifier = Symbol('Relation'); + + function recursive(contentFunctionName, ...args) { + const contentFunction = dependencies[contentFunctionName]; + if (!contentFunction) { + throw new Error(`Couldn't find dependency ${contentFunctionName}`); + } + + if (!contentFunction.relations) { + return null; + } + + const listedDependencies = new Set(contentFunction.contentDependencies); + + // TODO: Evaluating a sprawl might belong somewhere better than here, lol... + const sprawl = + (contentFunction.sprawl + ? contentFunction.sprawl(wikiData, ...args) + : null) + + const relationSlots = {}; + + const relationSymbolMessage = (() => { + let num = 1; + return name => `#${num++} ${name}`; + })(); + + const relationFunction = (name, ...args) => { + if (!listedDependencies.has(name)) { + throw new Error(`Called relation('${name}') but ${contentFunctionName} doesn't list that dependency`); + } + + const relationSymbol = Symbol(relationSymbolMessage(name)); + relationSlots[relationSymbol] = {name, args}; + return {[relationIdentifier]: relationSymbol}; + }; + + const relationsLayout = + (sprawl + ? contentFunction.relations(relationFunction, sprawl, ...args) + : contentFunction.relations(relationFunction, ...args)) + + const relationsTree = Object.fromEntries( + Object.getOwnPropertySymbols(relationSlots) + .map(symbol => [symbol, relationSlots[symbol]]) + .map(([symbol, {name, args}]) => [ + symbol, + recursive(name, ...args), + ])); + + return { + layout: relationsLayout, + slots: relationSlots, + tree: relationsTree, + }; + } + + const relationsTree = recursive(contentFunctionName, ...args); + + return { + root: { + name: contentFunctionName, + args, + relations: relationsTree?.layout, + }, + + relationIdentifier, + relationsTree, + }; +} + +export function flattenRelationsTree({ + root, + relationIdentifier, + relationsTree, +}) { + const flatRelationSlots = {}; + + function recursive({layout, slots, tree}) { + for (const slot of Object.getOwnPropertySymbols(slots)) { + if (tree[slot]) { + recursive(tree[slot]); + } + + flatRelationSlots[slot] = { + name: slots[slot].name, + args: slots[slot].args, + relations: tree[slot]?.layout ?? null, + }; + } + } + + if (relationsTree) { + recursive(relationsTree); + } + + return { + root, + relationIdentifier, + flatRelationSlots, + }; +} + +export function fillRelationsLayoutFromSlotResults(relationIdentifier, results, layout) { + function recursive(object) { + if (typeof object !== 'object' || object === null) { + return object; + } + + if (Array.isArray(object)) { + return object.map(recursive); + } + + if (relationIdentifier in object) { + return results[object[relationIdentifier]]; + } + + if (object.constructor !== Object) { + throw new Error(`Expected primitive, array, relation, or normal {key: value} style Object`); + } + + return Object.fromEntries( + Object.entries(object) + .map(([key, value]) => [key, recursive(value)])); + } + + return recursive(layout); +} + +export function getNeededContentDependencyNames(contentDependencies, name) { + const set = new Set(); + + function recursive(name) { + const contentFunction = contentDependencies[name]; + for (const dependencyName of contentFunction?.contentDependencies ?? []) { + recursive(dependencyName); + } + set.add(name); + } + + recursive(name); + + return set; +} + +export function quickEvaluate({ + contentDependencies: allContentDependencies, + extraDependencies: allExtraDependencies, + + name, + args = [], + multiple = null, + postprocess = null, +}) { + if (multiple !== null) { + return multiple.map(opts => + quickEvaluate({ + contentDependencies: allContentDependencies, + extraDependencies: allExtraDependencies, + + ...opts, + name: opts.name ?? name, + args: opts.args ?? args, + postprocess: opts.postprocess ?? postprocess, + })); + } + + const treeInfo = getRelationsTree(allContentDependencies, name, allExtraDependencies.wikiData ?? {}, ...args); + const flatTreeInfo = flattenRelationsTree(treeInfo); + const {root, relationIdentifier, flatRelationSlots} = flatTreeInfo; + + const neededContentDependencyNames = + getNeededContentDependencyNames(allContentDependencies, name); + + // Content functions aren't recursive, so by following the set above + // sequentually, we will always provide fulfilled content functions as the + // dependencies for later content functions. + const fulfilledContentDependencies = {}; + for (const name of neededContentDependencyNames) { + const unfulfilledContentFunction = allContentDependencies[name]; + if (!unfulfilledContentFunction) continue; + + const {contentDependencies, extraDependencies} = unfulfilledContentFunction; + + if (empty(contentDependencies) && empty(extraDependencies)) { + fulfilledContentDependencies[name] = unfulfilledContentFunction; + continue; + } + + const fulfillments = {}; + + for (const dependencyName of contentDependencies ?? []) { + if (dependencyName in fulfilledContentDependencies) { + fulfillments[dependencyName] = + fulfilledContentDependencies[dependencyName]; + } + } + + for (const dependencyName of extraDependencies ?? []) { + if (dependencyName in allExtraDependencies) { + fulfillments[dependencyName] = + allExtraDependencies[dependencyName]; + } + } + + fulfilledContentDependencies[name] = + unfulfilledContentFunction.fulfill(fulfillments); + } + + // There might still be unfulfilled content functions if dependencies weren't + // provided as part of allContentDependencies or allExtraDependencies. + // Catch and report these early, together in an aggregate error. + const unfulfilledErrors = []; + const unfulfilledNames = []; + for (const name of neededContentDependencyNames) { + const contentFunction = fulfilledContentDependencies[name]; + if (!contentFunction) continue; + if (!contentFunction.fulfilled) { + try { + contentFunction(); + } catch (error) { + error.message = `(${name}) ${error.message}`; + unfulfilledErrors.push(error); + unfulfilledNames.push(name); + } + } + } + + if (!empty(unfulfilledErrors)) { + throw new AggregateError(unfulfilledErrors, `Content functions unfulfilled (${unfulfilledNames.join(', ')})`); + } + + const slotResults = {}; + + function runContentFunction({name, args, relations: flatRelations}) { + const contentFunction = fulfilledContentDependencies[name]; + + if (!contentFunction) { + throw new Error(`Content function ${name} unfulfilled or not listed`); + } + + const sprawl = + contentFunction.sprawl?.(allExtraDependencies.wikiData, ...args); + + const relations = + fillRelationsLayoutFromSlotResults(relationIdentifier, slotResults, flatRelations); + + const data = + (sprawl + ? contentFunction.data?.(sprawl, ...args) + : contentFunction.data?.(...args)); + + const generateArgs = [data, relations].filter(Boolean); + + return contentFunction(...generateArgs); + } + + for (const slot of Object.getOwnPropertySymbols(flatRelationSlots)) { + slotResults[slot] = runContentFunction(flatRelationSlots[slot]); + } + + const topLevelResult = runContentFunction(root); + + if (postprocess !== null) { + return postprocess(topLevelResult); + } else { + return topLevelResult; + } +} diff --git a/src/content/dependencies/generateAdditionalFilesList.js b/src/content/dependencies/generateAdditionalFilesList.js new file mode 100644 index 00000000..eb9fc8b0 --- /dev/null +++ b/src/content/dependencies/generateAdditionalFilesList.js @@ -0,0 +1,121 @@ +import {empty} from '../../util/sugar.js'; + +export default { + extraDependencies: [ + 'html', + 'language', + ], + + data(additionalFiles, {fileSize = true} = {}) { + return { + // Additional files are already a serializable format. + additionalFiles, + showFileSizes: fileSize, + }; + }, + + generate(data, { + html, + language, + }) { + const fileKeys = data.additionalFiles.flatMap(({files}) => files); + const validateFileMapping = (v, validateValue) => { + return value => { + v.isObject(value); + + // It's OK to skip values for files, but if keys are provided for files + // which don't exist, that's an error. + + const unexpectedKeys = + Object.keys(value).filter(key => !fileKeys.includes(key)) + + if (!empty(unexpectedKeys)) { + throw new TypeError(`Unexpected file keys: ${unexpectedKeys.join(', ')}`); + } + + const valueErrors = []; + for (const [fileKey, fileValue] of Object.entries(value)) { + if (fileValue === null) { + continue; + } + + try { + validateValue(fileValue); + } catch (error) { + error.message = `(${fileKey}) ` + error.message; + valueErrors.push(error); + } + } + + if (!empty(valueErrors)) { + throw new AggregateError(valueErrors, `Errors validating values`); + } + }; + }; + + return html.template({ + annotation: 'generateAdditionalFilesList', + + slots: { + fileLinks: { + validate: v => validateFileMapping(v, v.isHTML), + }, + + fileSizes: { + validate: v => validateFileMapping(v, v.isWholeNumber), + }, + }, + + content(slots) { + if (!slots.fileSizes) { + return html.blank(); + } + + const filesWithLinks = new Set( + Object.entries(slots.fileLinks) + .filter(([key, value]) => value) + .map(([key]) => key)); + + if (filesWithLinks.size === 0) { + return html.blank(); + } + + const filteredFileGroups = data.additionalFiles + .map(({title, description, files}) => ({ + title, + description, + files: files.filter(f => filesWithLinks.has(f)), + })) + .filter(({files}) => !empty(files)); + + if (empty(filteredFileGroups)) { + return html.blank(); + } + + return html.tag('dl', + filteredFileGroups.flatMap(({title, description, files}) => [ + html.tag('dt', + (description + ? language.$('releaseInfo.additionalFiles.entry.withDescription', { + title, + description, + }) + : language.$('releaseInfo.additionalFiles.entry', {title}))), + + html.tag('dd', + html.tag('ul', + files.map(file => + html.tag('li', + (slots.fileSizes[file] + ? language.$('releaseInfo.additionalFiles.file.withSize', { + file: slots.fileLinks[file], + size: language.formatFileSize(slots.fileSizes[file]), + }) + : language.$('releaseInfo.additionalFiles.file', { + file: slots.fileLinks[file], + })))))), + ])); + }, + }); + }, +}; diff --git a/src/content/dependencies/generateAdditionalFilesShortcut.js b/src/content/dependencies/generateAdditionalFilesShortcut.js new file mode 100644 index 00000000..7dfe07b3 --- /dev/null +++ b/src/content/dependencies/generateAdditionalFilesShortcut.js @@ -0,0 +1,33 @@ +import {empty} from '../../util/sugar.js'; + +export default { + extraDependencies: [ + 'html', + 'language', + ], + + data(additionalFiles) { + return { + titles: additionalFiles.map(fileGroup => fileGroup.title), + }; + }, + + generate(data, { + html, + language, + }) { + if (empty(data.titles)) { + return html.blank(); + } + + return language.$('releaseInfo.additionalFiles.shortcut', { + anchorLink: + html.tag('a', + {href: '#additional-files'}, + language.$('releaseInfo.additionalFiles.shortcut.anchorLink')), + + titles: + language.formatUnitList(data.titles), + }); + }, +} diff --git a/src/content/dependencies/generateAlbumAdditionalFilesList.js b/src/content/dependencies/generateAlbumAdditionalFilesList.js new file mode 100644 index 00000000..5fd4e05b --- /dev/null +++ b/src/content/dependencies/generateAlbumAdditionalFilesList.js @@ -0,0 +1,57 @@ +export default { + contentDependencies: [ + 'generateAdditionalFilesList', + 'linkAlbumAdditionalFile', + ], + + extraDependencies: [ + 'getSizeOfAdditionalFile', + 'urls', + ], + + data(album, additionalFiles, {fileSize = true} = {}) { + return { + albumDirectory: album.directory, + fileLocations: additionalFiles.flatMap(({files}) => files), + showFileSizes: fileSize, + }; + }, + + relations(relation, album, additionalFiles, {fileSize = true} = {}) { + return { + additionalFilesList: + relation('generateAdditionalFilesList', additionalFiles, { + fileSize, + }), + + additionalFileLinks: + Object.fromEntries( + additionalFiles + .flatMap(({files}) => files) + .map(file => [ + file, + relation('linkAlbumAdditionalFile', album, file), + ])), + }; + }, + + generate(data, relations, { + getSizeOfAdditionalFile, + urls, + }) { + return relations.additionalFilesList + .slots({ + fileLinks: relations.additionalFileLinks, + fileSizes: + Object.fromEntries(data.fileLocations.map(file => [ + file, + (data.showFileSizes + ? getSizeOfAdditionalFile( + urls + .from('media.root') + .to('media.albumAdditionalFile', data.albumDirectory, file)) + : 0), + ])), + }); + }, +}; diff --git a/src/content/dependencies/generateAlbumInfoPage.js b/src/content/dependencies/generateAlbumInfoPage.js new file mode 100644 index 00000000..749dd2af --- /dev/null +++ b/src/content/dependencies/generateAlbumInfoPage.js @@ -0,0 +1,99 @@ +import getChronologyRelations from '../util/getChronologyRelations.js'; +import {sortAlbumsTracksChronologically} from '../../util/wiki-data.js'; + +export default { + contentDependencies: [ + 'generateAlbumInfoPageContent', + 'generateAlbumNavAccent', + 'generateAlbumSidebar', + 'generateAlbumSocialEmbed', + 'generateAlbumStyleRules', + 'generateChronologyLinks', + 'generateColorStyleRules', + 'generatePageLayout', + 'linkAlbum', + 'linkArtist', + 'linkTrack', + ], + + extraDependencies: ['language'], + + relations(relation, album) { + return { + layout: relation('generatePageLayout'), + + coverArtistChronologyContributions: getChronologyRelations(album, { + contributions: album.coverArtistContribs, + + linkArtist: artist => relation('linkArtist', artist), + + linkThing: trackOrAlbum => + (trackOrAlbum.album + ? relation('linkTrack', trackOrAlbum) + : relation('linkAlbum', trackOrAlbum)), + + getThings: artist => + sortAlbumsTracksChronologically([ + ...artist.albumsAsCoverArtist, + ...artist.tracksAsCoverArtist, + ]), + }), + + albumNavAccent: relation('generateAlbumNavAccent', album, null), + chronologyLinks: relation('generateChronologyLinks'), + + content: relation('generateAlbumInfoPageContent', album), + sidebar: relation('generateAlbumSidebar', album, null), + socialEmbed: relation('generateAlbumSocialEmbed', album), + albumStyleRules: relation('generateAlbumStyleRules', album), + colorStyleRules: relation('generateColorStyleRules', album.color), + }; + }, + + data(album) { + return { + name: album.name, + }; + }, + + generate(data, relations, {language}) { + return relations.layout + .slots({ + title: language.$('albumPage.title', {album: data.name}), + headingMode: 'sticky', + + colorStyleRules: [relations.colorStyleRules], + additionalStyleRules: [relations.albumStyleRules], + + cover: relations.content.cover, + mainContent: relations.content.main.content, + + navLinkStyle: 'hierarchical', + navLinks: [ + {auto: 'home'}, + { + auto: 'current', + accent: + relations.albumNavAccent.slots({ + showTrackNavigation: true, + showExtraLinks: true, + }), + }, + ], + + navContent: + relations.chronologyLinks.slots({ + chronologyInfoSets: [ + { + headingString: 'misc.chronology.heading.coverArt', + contributions: relations.coverArtistChronologyContributions, + }, + ], + }), + + ...relations.sidebar, + + // socialEmbed: relations.socialEmbed, + }); + }, +}; diff --git a/src/content/dependencies/generateAlbumInfoPageContent.js b/src/content/dependencies/generateAlbumInfoPageContent.js new file mode 100644 index 00000000..5d2817ee --- /dev/null +++ b/src/content/dependencies/generateAlbumInfoPageContent.js @@ -0,0 +1,307 @@ +import {accumulateSum, empty} from '../../util/sugar.js'; + +export default { + contentDependencies: [ + 'generateAdditionalFilesShortcut', + 'generateAlbumAdditionalFilesList', + 'generateAlbumTrackList', + 'generateContentHeading', + 'generateCoverArtwork', + 'linkAlbumCommentary', + 'linkAlbumGallery', + 'linkContribution', + 'linkExternal', + 'transformContent', + ], + + extraDependencies: ['html', 'language'], + + relations(relation, album) { + const relations = {}; + const sections = relations.sections = {}; + + const contributionLinksRelation = contribs => + contribs.map(contrib => + relation('linkContribution', contrib.who, contrib.what)); + + // Section: Release info + + const releaseInfo = sections.releaseInfo = {}; + + if (!empty(album.artistContribs)) { + releaseInfo.artistContributionLinks = + contributionLinksRelation(album.artistContribs); + } + + if (album.hasCoverArt) { + relations.cover = + relation('generateCoverArtwork', album.artTags); + releaseInfo.coverArtistContributionLinks = + contributionLinksRelation(album.coverArtistContribs); + } else { + relations.cover = null; + } + + if (album.hasWallpaperArt) { + releaseInfo.wallpaperArtistContributionLinks = + contributionLinksRelation(album.wallpaperArtistContribs); + } + + if (album.hasBannerArt) { + releaseInfo.bannerArtistContributionLinks = + contributionLinksRelation(album.bannerArtistContribs); + } + + // Section: Listen on + + if (!empty(album.urls)) { + const listen = sections.listen = {}; + + listen.externalLinks = + album.urls.map(url => + relation('linkExternal', url, {type: 'album'})); + } + + // Section: Extra links + + const extra = sections.extra = {}; + + if (album.tracks.some(t => t.hasUniqueCoverArt)) { + extra.galleryLink = + relation('linkAlbumGallery', album); + } + + if (album.commentary || album.tracks.some(t => t.commentary)) { + extra.commentaryLink = + relation('linkAlbumCommentary', album); + } + + if (!empty(album.additionalFiles)) { + extra.additionalFilesShortcut = + relation('generateAdditionalFilesShortcut', album.additionalFiles); + } + + // Section: Track list + + relations.trackList = + relation('generateAlbumTrackList', album); + + // Section: Additional files + + if (!empty(album.additionalFiles)) { + const additionalFiles = sections.additionalFiles = {}; + + additionalFiles.heading = + relation('generateContentHeading'); + + additionalFiles.additionalFilesList = + relation('generateAlbumAdditionalFilesList', album, album.additionalFiles); + } + + // Section: Artist commentary + + if (album.commentary) { + const artistCommentary = sections.artistCommentary = {}; + + artistCommentary.heading = + relation('generateContentHeading'); + + artistCommentary.content = + relation('transformContent', album.commentary); + } + + return relations; + }, + + data(album) { + const data = {}; + + data.date = album.date; + data.duration = accumulateSum(album.tracks, track => track.duration); + data.durationApproximate = album.tracks.length > 1; + + data.hasCoverArt = album.hasCoverArt; + + if (album.hasCoverArt) { + data.coverArtDirectory = album.directory; + data.coverArtFileExtension = album.coverArtFileExtension; + + if (album.coverArtDate && +album.coverArtDate !== +album.date) { + data.coverArtDate = album.coverArtDate; + } + } + + if (!empty(album.additionalFiles)) { + data.numAdditionalFiles = album.additionalFiles.length; + } + + data.dateAddedToWiki = album.dateAddedToWiki; + + return data; + }, + + generate(data, relations, { + html, + language, + }) { + const content = {}; + + const {sections: sec} = relations; + + const formatContributions = + (stringKey, contributionLinks, {showContribution = true, showIcons = true} = {}) => + contributionLinks && + language.$(stringKey, { + artists: + language.formatConjunctionList( + contributionLinks.map(link => + link.slots({showContribution, showIcons}))), + }); + + if (data.hasCoverArt) { + content.cover = relations.cover + .slots({ + path: ['media.albumCover', data.coverArtDirectory, data.coverArtFileExtension], + alt: language.$('misc.alt.trackCover') + }); + } else { + content.cover = null; + } + + content.main = { + headingMode: 'sticky', + content: html.tags([ + html.tag('p', + { + [html.onlyIfContent]: true, + [html.joinChildren]: html.tag('br'), + }, + [ + formatContributions('releaseInfo.by', sec.releaseInfo.artistContributionLinks), + formatContributions('releaseInfo.coverArtBy', sec.releaseInfo.coverArtistContributionLinks), + formatContributions('releaseInfo.wallpaperArtBy', sec.releaseInfo.wallpaperArtistContributionLinks), + formatContributions('releaseInfo.bannerArtBy', sec.releaseInfo.bannerArtistContributionLinks), + + data.date && + language.$('releaseInfo.released', { + date: language.formatDate(data.date), + }), + + data.coverArtDate && + language.$('releaseInfo.artReleased', { + date: language.formatDate(data.coverArtDate), + }), + + data.duration && + language.$('releaseInfo.duration', { + duration: + language.formatDuration(data.duration, { + approximate: data.durationApproximate, + }), + }), + ]), + + sec.listen && + html.tag('p', + language.$('releaseInfo.listenOn', { + links: language.formatDisjunctionList(sec.listen.externalLinks), + })), + + html.tag('p', + { + [html.onlyIfContent]: true, + [html.joinChildren]: html.tag('br'), + }, + [ + sec.extra.additionalFilesShortcut, + + sec.extra.galleryLink && sec.extra.commentaryLink && + language.$('releaseInfo.viewGalleryOrCommentary', { + gallery: + sec.extra.galleryLink + .slot('content', language.$('releaseInfo.viewGalleryOrCommentary.gallery')), + commentary: + sec.extra.commentaryLink + .slot('content', language.$('releaseInfo.viewGalleryOrCommentary.commentary')), + }), + + sec.extra.galleryLink && !sec.extra.commentaryLink && + language.$('releaseInfo.viewGallery', { + link: + sec.extra.galleryLink + .slot('content', language.$('releaseInfo.viewGallery.link')), + }), + + !sec.extra.galleryLink && sec.extra.commentaryLink && + language.$('releaseInfo.viewCommentary', { + link: + sec.extra.commentaryLink + .slot('content', language.$('releaseInfo.viewCommentary.link')), + }), + ]), + + relations.trackList, + + html.tag('p', + { + [html.onlyIfContent]: true, + [html.joinChildren]: '<br>', + }, + [ + data.dateAddedToWiki && + language.$('releaseInfo.addedToWiki', { + date: language.formatDate(data.dateAddedToWiki), + }), + ]), + + sec.additionalFiles && [ + sec.additionalFiles.heading + .slots({ + id: 'additional-files', + title: + language.$('releaseInfo.additionalFiles.heading', { + additionalFiles: + language.countAdditionalFiles(data.numAdditionalFiles, {unit: true}), + }), + }), + + sec.additionalFiles.additionalFilesList, + ], + + sec.artistCommentary && [ + sec.artistCommentary.heading + .slots({ + id: 'artist-commentary', + title: language.$('releaseInfo.artistCommentary') + }), + + html.tag('blockquote', + sec.artistCommentary.content + .slot('mode', 'multiline')), + ], + ]), + }; + + return content; + }, +}; + +/* + banner: !empty(album.bannerArtistContribs) && { + dimensions: album.bannerDimensions, + path: [ + 'media.albumBanner', + album.directory, + album.bannerFileExtension, + ], + alt: language.$('misc.alt.albumBanner'), + position: 'top', + }, + + secondaryNav: generateAlbumSecondaryNav(album, null, { + getLinkThemeString, + html, + language, + link, + }), +*/ diff --git a/src/content/dependencies/generateAlbumNavAccent.js b/src/content/dependencies/generateAlbumNavAccent.js new file mode 100644 index 00000000..9d1d87c3 --- /dev/null +++ b/src/content/dependencies/generateAlbumNavAccent.js @@ -0,0 +1,120 @@ +import {empty} from '../../util/sugar.js'; + +export default { + contentDependencies: [ + 'generatePreviousNextLinks', + 'linkTrack', + 'linkAlbumCommentary', + 'linkAlbumGallery', + ], + + extraDependencies: ['html', 'language'], + + relations(relation, album, track) { + const relations = {}; + + relations.previousNextLinks = + relation('generatePreviousNextLinks'); + + relations.previousTrackLink = null; + relations.nextTrackLink = null; + + if (track) { + const index = album.tracks.indexOf(track); + + if (index > 0) { + relations.previousTrackLink = + relation('linkTrack', album.tracks[index - 1]); + } + + if (index < album.tracks.length - 1) { + relations.nextTrackLink = + relation('linkTrack', album.tracks[index + 1]); + } + } + + if (album.tracks.some(t => t.hasUniqueCoverArt)) { + relations.albumGalleryLink = + relation('linkAlbumGallery', album); + } + + if (album.commentary || album.tracks.some(t => t.commentary)) { + relations.albumCommentaryLink = + relation('linkAlbumCommentary', album); + } + + return relations; + }, + + data(album, track) { + return { + hasMultipleTracks: album.tracks.length > 1, + isTrackPage: !!track, + }; + }, + + generate(data, relations, {html, language}) { + return html.template({ + annotation: `generateAlbumNavAccent`, + + slots: { + showTrackNavigation: {type: 'boolean', default: false}, + showExtraLinks: {type: 'boolean', default: false}, + + currentExtra: { + validate: v => v.is('gallery', 'commentary'), + }, + }, + + content(slots) { + const {content: extraLinks = []} = + slots.showExtraLinks && + {content: [ + relations.albumGalleryLink?.slots({ + attributes: {class: slots.currentExtra === 'gallery' && 'current'}, + content: language.$('albumPage.nav.gallery'), + }), + + relations.albumCommentaryLink?.slots({ + attributes: {class: slots.currentExtra === 'commentary' && 'current'}, + content: language.$('albumPage.nav.commentary'), + }), + ]}; + + const {content: previousNextLinks = []} = + slots.showTrackNavigation && + data.isTrackPage && + data.hasMultipleTracks && + relations.previousNextLinks.slots({ + previousLink: relations.previousTrackLink, + nextLink: relations.nextTrackLink, + }); + + const randomLink = + slots.showTrackNavigation && + data.hasMultipleTracks && + html.tag('a', + { + href: '#', + 'data-random': 'track-in-album', + id: 'random-button', + }, + (data.isTrackPage + ? language.$('trackPage.nav.random') + : language.$('albumPage.nav.randomTrack'))); + + const allLinks = [ + ...previousNextLinks, + ...extraLinks, + randomLink, + ].filter(Boolean); + + if (empty(allLinks)) { + return html.blank(); + } + + return `(${language.formatUnitList(allLinks)})` + }, + }); + }, +}; diff --git a/src/content/dependencies/generateAlbumSidebar.js b/src/content/dependencies/generateAlbumSidebar.js new file mode 100644 index 00000000..bf6b091a --- /dev/null +++ b/src/content/dependencies/generateAlbumSidebar.js @@ -0,0 +1,76 @@ +export default { + contentDependencies: [ + 'generateAlbumSidebarGroupBox', + 'generateAlbumSidebarTrackSection', + 'linkAlbum', + ], + + extraDependencies: ['html'], + + + relations(relation, album, track) { + const relations = {}; + + relations.albumLink = + relation('linkAlbum', album); + + relations.groupBoxes = + album.groups.map(group => + relation('generateAlbumSidebarGroupBox', album, group)); + + relations.trackSections = + album.trackSections.map(trackSection => + relation('generateAlbumSidebarTrackSection', album, track, trackSection)); + + return relations; + }, + + data(album, track) { + return {isAlbumPage: !track}; + }, + + generate(data, relations, {html}) { + const trackListBox = { + content: + html.tags([ + html.tag('h1', relations.albumLink), + relations.trackSections, + ]), + }; + + if (data.isAlbumPage) { + const groupBoxes = + relations.groupBoxes + .map(content => content.slot('isAlbumPage', true)) + .map(content => ({content})); + + return { + leftSidebarMultiple: [ + ...groupBoxes, + trackListBox, + ], + }; + } + + const conjoinedGroupBox = { + content: + relations.groupBoxes + .flatMap((content, i, {length}) => [ + content, + i < length - 1 && + html.tag('hr', { + style: `border-color: var(--primary-color); border-style: none none dotted none` + }), + ]) + .filter(Boolean), + }; + + return { + // leftSidebarStickyMode: 'column', + leftSidebarMultiple: [ + trackListBox, + conjoinedGroupBox, + ], + }; + }, +}; diff --git a/src/content/dependencies/generateAlbumSidebarGroupBox.js b/src/content/dependencies/generateAlbumSidebarGroupBox.js new file mode 100644 index 00000000..1c27af9e --- /dev/null +++ b/src/content/dependencies/generateAlbumSidebarGroupBox.js @@ -0,0 +1,88 @@ +import {empty} from '../../util/sugar.js'; + +export default { + contentDependencies: [ + 'linkAlbum', + 'linkExternal', + 'linkGroup', + 'transformContent', + ], + + extraDependencies: ['html', 'language'], + + relations(relation, album, group) { + const relations = {}; + + relations.groupLink = + relation('linkGroup', group); + + relations.externalLinks = + group.urls.map(url => + relation('linkExternal', url)); + + const albums = group.albums.filter(album => album.date); + const index = albums.indexOf(album); + const previousAlbum = (index > 0) && albums[index - 1]; + const nextAlbum = (index < albums.length - 1) && albums[index + 1]; + + if (group.descriptionShort) { + relations.description = + relation('transformContent', group.descriptionShort); + } + + if (previousAlbum) { + relations.previousAlbumLink = + relation('linkAlbum', previousAlbum); + } + + if (nextAlbum) { + relations.nextAlbumLink = + relation('linkAlbum', nextAlbum); + } + + return relations; + }, + + generate(relations, {html, language}) { + return html.template({ + annotation: `generateAlbumSidebarGroupBox`, + + slots: { + isAlbumPage: {type: 'boolean', default: false}, + }, + + content(slots) { + return html.tags([ + html.tag('h1', + language.$('albumSidebar.groupBox.title', { + group: relations.groupLink, + })), + + slots.isAlbumPage && + relations.description + ?.slot('mode', 'multiline'), + + !empty(relations.externalLinks) && + html.tag('p', + language.$('releaseInfo.visitOn', { + links: language.formatDisjunctionList(relations.externalLinks), + })), + + slots.isAlbumPage && + relations.nextAlbumLink && + html.tag('p', {class: 'group-chronology-link'}, + language.$('albumSidebar.groupBox.next', { + album: relations.nextAlbumLink, + })), + + slots.isAlbumPage && + relations.previousAlbumLink && + html.tag('p', {class: 'group-chronology-link'}, + language.$('albumSidebar.groupBox.previous', { + album: relations.previousAlbumLink, + })), + ]); + }, + }); + }, +}; diff --git a/src/content/dependencies/generateAlbumSidebarTrackSection.js b/src/content/dependencies/generateAlbumSidebarTrackSection.js new file mode 100644 index 00000000..2aca6da1 --- /dev/null +++ b/src/content/dependencies/generateAlbumSidebarTrackSection.js @@ -0,0 +1,98 @@ +export default { + contentDependencies: ['linkTrack'], + extraDependencies: ['getColors', 'html', 'language'], + + relations(relation, album, track, trackSection) { + const relations = {}; + + relations.trackLinks = + trackSection.tracks.map(track => + relation('linkTrack', track)); + + return relations; + }, + + data(album, track, trackSection) { + const data = {}; + + data.hasTrackNumbers = album.hasTrackNumbers; + data.isTrackPage = !!track; + + data.name = trackSection.name; + data.color = trackSection.color; + data.isDefaultTrackSection = trackSection.isDefaultTrackSection; + + data.firstTrackNumber = trackSection.startIndex + 1; + data.lastTrackNumber = trackSection.startIndex + trackSection.tracks.length; + + if (track) { + const index = trackSection.tracks.indexOf(track); + if (index !== -1) { + data.includesCurrentTrack = true; + data.currentTrackIndex = index; + } + } + + return data; + }, + + generate(data, relations, {getColors, html, language}) { + const sectionName = + html.tag('span', {class: 'group-name'}, + (data.isDefaultTrackSection + ? language.$('albumSidebar.trackList.fallbackSectionName') + : data.name)); + + let style; + if (data.color) { + const {primary} = getColors(data.color); + style = `--primary-color: ${primary}`; + } + + const trackListItems = + relations.trackLinks.map((trackLink, index) => + html.tag('li', + { + class: + data.includesCurrentTrack && + index === data.currentTrackIndex && + 'current', + }, + language.$('albumSidebar.trackList.item', { + track: trackLink, + }))); + + return html.tag('details', + { + class: data.includesCurrentTrack && 'current', + + open: ( + // Leave sidebar track sections collapsed on album info page, + // since there's already a view of the full track listing + // in the main content area. + data.isTrackPage && + + // Only expand the track section which includes the track + // currently being viewed by default. + data.includesCurrentTrack), + }, + [ + html.tag('summary', {style}, + html.tag('span', + (data.hasTrackNumbers + ? language.$('albumSidebar.trackList.group.withRange', { + group: sectionName, + range: `${data.firstTrackNumber}–${data.lastTrackNumber}` + }) + : language.$('albumSidebar.trackList.group', { + group: sectionName, + })))), + + (data.hasTrackNumbers + ? html.tag('ol', + {start: data.firstTrackNumber}, + trackListItems) + : html.tag('ul', trackListItems)), + ]); + }, +}; diff --git a/src/content/dependencies/generateAlbumSocialEmbed.js b/src/content/dependencies/generateAlbumSocialEmbed.js new file mode 100644 index 00000000..656bd997 --- /dev/null +++ b/src/content/dependencies/generateAlbumSocialEmbed.js @@ -0,0 +1,85 @@ +import {empty} from '../../util/sugar.js'; + +export default { + contentDependencies: [ + 'generateAlbumSocialEmbedDescription', + ], + + extraDependencies: [ + 'absoluteTo', + 'language', + 'urls', + ], + + relations(relation, album) { + const relations = {}; + + relations.description = + relation('generateAlbumSocialEmbedDescription', album); + + return relations; + }, + + data(album) { + const data = {}; + + data.hasHeading = !empty(album.groups); + + if (data.hasHeading) { + const firstGroup = album.groups[0]; + data.headingGroupName = firstGroup.directory; + data.headingGroupDirectory = firstGroup.directory; + } + + data.hasImage = album.hasCoverArt; + + if (data.hasImage) { + data.coverArtDirectory = album.directory; + data.coverArtFileExtension = album.coverArtFileExtension; + } + + data.albumName = album.name; + data.albumColor = album.color; + + return data; + }, + + generate(data, relations, { + absoluteTo, + language, + urls, + }) { + const socialEmbed = {}; + + if (data.hasHeading) { + socialEmbed.heading = + language.$('albumPage.socialEmbed.heading', { + group: data.headingGroupName, + }); + + socialEmbed.headingLink = + absoluteTo('localized.album', data.headingGroupDirectory); + } else { + socialEmbed.heading = ''; + socialEmbed.headingLink = null; + } + + socialEmbed.title = + language.$('albumPage.socialEmbed.title', { + album: data.albumName, + }); + + socialEmbed.description = relations.description; + + if (data.hasImage) { + const imagePath = urls + .from('shared.root') + .to('media.albumCover', data.coverArtDirectory, data.coverArtFileExtension); + socialEmbed.image = '/' + imagePath; + } + + socialEmbed.color = data.albumColor; + + return socialEmbed; + }, +}; diff --git a/src/content/dependencies/generateAlbumSocialEmbedDescription.js b/src/content/dependencies/generateAlbumSocialEmbedDescription.js new file mode 100644 index 00000000..5fa67b26 --- /dev/null +++ b/src/content/dependencies/generateAlbumSocialEmbedDescription.js @@ -0,0 +1,50 @@ +import {accumulateSum} from '../../util/sugar.js'; + +export default { + extraDependencies: ['language'], + + data(album) { + const data = {}; + + const duration = accumulateSum(album.tracks, track => track.duration); + + data.hasDuration = duration > 0; + data.hasTracks = album.tracks.length > 0; + data.hasDate = !!album.date; + data.hasAny = (data.hasDuration || data.hasTracks || data.hasDuration); + + if (!data.hasAny) + return data; + + if (data.hasDuration) + data.duration = duration; + + if (data.hasTracks) + data.tracks = album.tracks.length; + + if (data.hasDate) + data.date = album.date; + + return data; + }, + + generate(data, { + language, + }) { + return language.formatString( + 'albumPage.socialEmbed.body' + [ + data.hasDuration && '.withDuration', + data.hasTracks && '.withTracks', + data.hasDate && '.withReleaseDate', + ].filter(Boolean).join(''), + + Object.fromEntries([ + data.hasDuration && + ['duration', language.formatDuration(data.duration)], + data.hasTracks && + ['tracks', language.countTracks(data.tracks, {unit: true})], + data.hasDate && + ['date', language.formatDate(data.date)], + ].filter(Boolean))); + }, +}; diff --git a/src/content/dependencies/generateAlbumStyleRules.js b/src/content/dependencies/generateAlbumStyleRules.js new file mode 100644 index 00000000..c9547836 --- /dev/null +++ b/src/content/dependencies/generateAlbumStyleRules.js @@ -0,0 +1,61 @@ +import {empty} from '../../util/sugar.js'; + +export default { + extraDependencies: [ + 'to', + ], + + data(album) { + const data = {}; + + data.hasWallpaper = !empty(album.wallpaperArtistContribs); + data.hasBanner = !empty(album.bannerArtistContribs); + + if (data.hasWallpaper) { + data.hasWallpaperStyle = !!album.wallpaperStyle; + data.wallpaperPath = ['media.albumWallpaper', album.directory, album.wallpaperFileExtension]; + data.wallpaperStyle = album.wallpaperStyle; + } + + if (data.hasBanner) { + data.hasBannerStyle = !!album.bannerStyle; + data.bannerStyle = album.bannerStyle; + } + + return data; + }, + + generate(data, {to}) { + const wallpaperPart = + (data.hasWallpaper + ? [ + `body::before {`, + ` background-image: url("${to(...data.wallpaperPath)}");`, + ...(data.hasWallpaperStyle + ? data.wallpaperStyle + .split('\n') + .map(line => ` ${line}`) + : []), + `}`, + ] + : []); + + const bannerPart = + (data.hasBannerStyle + ? [ + `#banner img {`, + ...data.bannerStyle + .split('\n') + .map(line => ` ${line}`), + `}`, + ] + : []); + + return [ + ...wallpaperPart, + ...bannerPart, + ] + .filter(Boolean) + .join('\n'); + }, +}; diff --git a/src/content/dependencies/generateAlbumTrackList.js b/src/content/dependencies/generateAlbumTrackList.js new file mode 100644 index 00000000..ce174953 --- /dev/null +++ b/src/content/dependencies/generateAlbumTrackList.js @@ -0,0 +1,126 @@ +import {accumulateSum, empty} from '../../util/sugar.js'; + +function displayTrackSections(album) { + if (empty(album.trackSections)) { + return false; + } + + if (album.trackSections.length > 1) { + return true; + } + + if (!album.trackSections[0].isDefaultTrackSection) { + return true; + } + + return false; +} + +function displayTracks(album) { + if (empty(album.tracks)) { + return false; + } + + return true; +} + +function getDisplayMode(album) { + if (displayTrackSections(album)) { + return 'trackSections'; + } else if (displayTracks(album)) { + return 'tracks'; + } else { + return 'none'; + } +} + +export default { + contentDependencies: [ + 'generateAlbumTrackListItem', + ], + + extraDependencies: [ + 'html', + 'language', + ], + + relations(relation, album) { + const relations = {}; + + const displayMode = getDisplayMode(album); + + if (displayMode === 'trackSections') { + relations.itemsByTrackSection = + album.trackSections.map(section => + section.tracks.map(track => + relation('generateAlbumTrackListItem', track, album))); + } + + if (displayMode === 'tracks') { + relations.itemsByTrack = + album.tracks.map(track => + relation('generateAlbumTrackListItem', track, album)); + } + + return relations; + }, + + data(album) { + const data = {}; + + data.hasTrackNumbers = album.hasTrackNumbers; + + if (displayTrackSections && !empty(album.trackSections)) { + data.trackSectionInfo = + album.trackSections.map(section => { + const info = {}; + + info.name = section.name; + info.duration = accumulateSum(section.tracks, track => track.duration); + info.durationApproximate = section.tracks.length > 1; + + if (album.hasTrackNumbers) { + info.startIndex = section.startIndex; + } + + return info; + }); + } + + return data; + }, + + generate(data, relations, { + html, + language, + }) { + const listTag = (data.hasTrackNumbers ? 'ol' : 'ul'); + + if (relations.itemsByTrackSection) { + return html.tag('dl', + {class: 'album-group-list'}, + data.trackSectionInfo.map((info, index) => [ + html.tag('dt', + {class: 'content-heading', tabindex: '0'}, + language.$('trackList.section.withDuration', { + section: info.name, + duration: + language.formatDuration(info.duration, { + approximate: info.durationApproximate, + }), + })), + + html.tag('dd', + html.tag(listTag, + data.hasTrackNumbers ? {start: info.startIndex + 1} : {}, + relations.itemsByTrackSection[index])), + ])); + } + + if (relations.itemsByTrack) { + return html.tag(listTag, relations.itemsByTrack); + } + + return html.blank(); + } +}; diff --git a/src/content/dependencies/generateAlbumTrackListItem.js b/src/content/dependencies/generateAlbumTrackListItem.js new file mode 100644 index 00000000..fe46153d --- /dev/null +++ b/src/content/dependencies/generateAlbumTrackListItem.js @@ -0,0 +1,75 @@ +import {compareArrays} from '../../util/sugar.js'; + +export default { + contentDependencies: [ + 'linkContribution', + 'linkTrack', + ], + + extraDependencies: [ + 'getColors', + 'html', + 'language', + ], + + relations(relation, track) { + const relations = {}; + + relations.contributionLinks = + track.artistContribs.map(({who, what}) => + relation('linkContribution', who, what, { + showContribution: false, + showIcons: false, + })); + + relations.trackLink = + relation('linkTrack', track); + + return relations; + }, + + data(track, album) { + const data = {}; + + data.color = track.color; + data.duration = track.duration ?? 0; + + data.showArtists = + !compareArrays( + track.artistContribs.map(c => c.who), + album.artistContribs.map(c => c.who), + {checkOrder: false}); + + return data; + }, + + generate(data, relations, { + getColors, + html, + language, + }) { + const stringOpts = { + duration: language.formatDuration(data.duration), + track: relations.trackLink, + }; + + let style; + if (data.color) { + const {primary} = getColors(data.color); + style = `--primary-color: ${primary}`; + } + + return html.tag('li', + {style}, + (!data.showArtists + ? language.$('trackList.item.withDuration', stringOpts) + : language.$('trackList.item.withDuration.withArtists', { + ...stringOpts, + by: + html.tag('span', {class: 'by'}, + language.$('trackList.item.withArtists.by', { + artists: language.formatConjunctionList(relations.contributionLinks), + })), + }))); + }, +}; diff --git a/src/content/dependencies/generateArtistInfoPage.js b/src/content/dependencies/generateArtistInfoPage.js new file mode 100644 index 00000000..a91eebf2 --- /dev/null +++ b/src/content/dependencies/generateArtistInfoPage.js @@ -0,0 +1,818 @@ +import {empty, filterProperties, unique} from '../../util/sugar.js'; + +import { + chunkByProperties, + getTotalDuration, + sortAlbumsTracksChronologically, +} from '../../util/wiki-data.js'; + +export default { + contentDependencies: [ + 'generateArtistNavLinks', + 'generateContentHeading', + 'generatePageLayout', + 'linkAlbum', + 'linkArtist', + 'linkArtistGallery', + 'linkExternal', + 'linkGroup', + 'linkTrack', + 'transformContent', + ], + + extraDependencies: ['html', 'language'], + + relations(relation, artist) { + const relations = {}; + const sections = relations.sections = {}; + + relations.layout = + relation('generatePageLayout'); + + relations.artistNavLinks = + relation('generateArtistNavLinks', artist); + + const processContribs = (...contribArrays) => { + const properties = {}; + + const ownContribs = + contribArrays + .map(contribs => contribs.find(({who}) => who === artist)) + .filter(Boolean); + + const contributionDescriptions = + ownContribs + .map(({what}) => what) + .filter(Boolean); + + if (!empty(contributionDescriptions)) { + properties.contributionDescriptions = contributionDescriptions; + } + + const otherArtistContribs = + contribArrays + .map(contribs => contribs.filter(({who}) => who !== artist)) + .flat(); + + if (!empty(otherArtistContribs)) { + properties.otherArtistLinks = + otherArtistContribs + .map(({who}) => relation('linkArtist', who)); + } + + return properties; + }; + + const sortContributionEntries = (entries, sortFunction) => { + const things = unique(entries.map(({thing}) => thing)); + sortFunction(things); + + const outputArrays = []; + const thingToOutputArray = new Map(); + + for (const thing of things) { + const array = []; + thingToOutputArray.set(thing, array); + outputArrays.push(array); + } + + for (const entry of entries) { + thingToOutputArray.get(entry.thing).push(entry); + } + + entries.splice(0, entries.length, ...outputArrays.flat()); + }; + + const getGroupInfo = (entries) => { + const allGroups = new Set(); + const groupToDuration = new Map(); + const groupToCount = new Map(); + + for (const entry of entries) { + for (const group of entry.album.groups) { + allGroups.add(group); + groupToCount.set(group, (groupToCount.get(group) ?? 0) + 1); + groupToDuration.set(group, (groupToDuration.get(group) ?? 0) + entry.duration ?? 0); + } + } + + const groupInfo = + Array.from(allGroups) + .map(group => ({ + groupLink: relation('linkGroup', group), + duration: groupToDuration.get(group) ?? 0, + count: groupToCount.get(group), + })); + + groupInfo.sort((a, b) => b.count - a.count); + groupInfo.sort((a, b) => b.duration - a.duration); + + return groupInfo; + }; + + if (artist.contextNotes) { + const contextNotes = sections.contextNotes = {}; + contextNotes.content = relation('transformContent', artist.contextNotes); + } + + if (!empty(artist.urls)) { + const visit = sections.visit = {}; + visit.externalLinks = + artist.urls.map(url => + relation('linkExternal', url)); + } + + const trackContributionEntries = [ + ...artist.tracksAsArtist.map(track => ({ + date: track.date, + thing: track, + album: track.album, + duration: track.duration, + rerelease: track.originalReleaseTrack !== null, + trackLink: relation('linkTrack', track), + ...processContribs(track.artistContribs), + })), + + ...artist.tracksAsContributor.map(track => ({ + date: track.date, + thing: track, + album: track.album, + duration: track.duration, + rerelease: track.originalReleaseTrack !== null, + trackLink: relation('linkTrack', track), + ...processContribs(track.contributorContribs), + })), + ]; + + sortContributionEntries(trackContributionEntries, sortAlbumsTracksChronologically); + + const trackContributionChunks = + chunkByProperties(trackContributionEntries, ['album', 'date']) + .map(({album, date, chunk}) => ({ + albumLink: relation('linkAlbum', album), + date: +date, + duration: getTotalDuration(chunk), + entries: chunk + .map(entry => + filterProperties(entry, [ + 'contributionDescriptions', + 'duration', + 'otherArtistLinks', + 'rerelease', + 'trackLink', + ])), + })); + + const trackGroupInfo = getGroupInfo(trackContributionEntries, 'duration'); + + if (!empty(trackContributionChunks)) { + const tracks = sections.tracks = {}; + tracks.heading = relation('generateContentHeading'); + tracks.chunks = trackContributionChunks; + + if (!empty(trackGroupInfo)) { + tracks.groupInfo = trackGroupInfo; + } + } + + // TODO: Add and integrate wallpaper and banner date fields (#90) + const artContributionEntries = [ + ...artist.albumsAsCoverArtist.map(album => ({ + kind: 'albumCover', + date: album.coverArtDate, + thing: album, + album: album, + ...processContribs(album.coverArtistContribs), + })), + + ...artist.albumsAsWallpaperArtist.map(album => ({ + kind: 'albumWallpaper', + date: album.coverArtDate, + thing: album, + album: album, + ...processContribs(album.wallpaperArtistContribs), + })), + + ...artist.albumsAsBannerArtist.map(album => ({ + kind: 'albumBanner', + date: album.coverArtDate, + thing: album, + album: album, + ...processContribs(album.bannerArtistContribs), + })), + + ...artist.tracksAsCoverArtist.map(track => ({ + kind: 'trackCover', + date: track.coverArtDate, + thing: track, + album: track.album, + rerelease: track.originalReleaseTrack !== null, + trackLink: relation('linkTrack', track), + ...processContribs(track.coverArtistContribs), + })), + ]; + + sortContributionEntries(artContributionEntries, sortAlbumsTracksChronologically); + + const artContributionChunks = + chunkByProperties(artContributionEntries, ['album', 'date']) + .map(({album, date, chunk}) => ({ + albumLink: relation('linkAlbum', album), + date: +date, + entries: + chunk.map(entry => + filterProperties(entry, [ + 'contributionDescriptions', + 'kind', + 'otherArtistLinks', + 'rerelease', + 'trackLink', + ])), + })); + + const artGroupInfo = getGroupInfo(artContributionEntries, 'count'); + + if (!empty(artContributionChunks)) { + const artworks = sections.artworks = {}; + artworks.heading = relation('generateContentHeading'); + artworks.chunks = artContributionChunks; + + if ( + !empty(artist.albumsAsCoverArtist) || + !empty(artist.tracksAsCoverArtist) + ) { + artworks.artistGalleryLink = + relation('linkArtistGallery', artist); + } + + if (!empty(artGroupInfo)) { + artworks.groupInfo = artGroupInfo; + } + } + + // Commentary doesn't use the detailed contribution system where multiple + // artists are collaboratively credited for the same piece, so there isn't + // really anything special to do for processing or presenting it. + + const commentaryEntries = [ + ...artist.albumsAsCommentator.map(album => ({ + kind: 'albumCommentary', + date: album.date, + thing: album, + album: album, + })), + + ...artist.tracksAsCommentator.map(track => ({ + kind: 'trackCommentary', + date: track.date, + thing: track, + album: track.album, + trackLink: relation('linkTrack', track), + })), + ]; + + sortContributionEntries(commentaryEntries, sortAlbumsTracksChronologically); + + // We still pass through (and chunk by) date here, even though it doesn't + // actually get displayed on the album page. See issue #193. + const commentaryChunks = + chunkByProperties(commentaryEntries, ['album', 'date']) + .map(({album, date, chunk}) => ({ + albumLink: relation('linkAlbum', album), + date: +date, + entries: + chunk.map(entry => + filterProperties(entry, [ + 'kind', + 'trackLink', + ])), + })); + + if (!empty(commentaryChunks)) { + const commentary = sections.commentary = {}; + commentary.heading = relation('generateContentHeading'); + commentary.chunks = commentaryChunks; + } + + return relations; + }, + + data(artist) { + const data = {}; + + data.name = artist.name; + + const allTracks = unique([...artist.tracksAsArtist, ...artist.tracksAsContributor]); + data.totalTrackCount = allTracks.length; + data.totalDuration = getTotalDuration(allTracks, {originalReleasesOnly: true}); + + return data; + }, + + generate(data, relations, {html, language}) { + const {sections: sec} = relations; + + const addAccentsToEntry = ({ + rerelease, + entry, + otherArtistLinks, + contributionDescriptions, + }) => { + if (rerelease) { + return language.$('artistPage.creditList.entry.rerelease', {entry}); + } + + const options = {entry}; + const parts = ['artistPage.creditList.entry']; + + if (otherArtistLinks) { + parts.push('withArtists'); + options.artists = language.formatConjunctionList(otherArtistLinks); + } + + if (contributionDescriptions) { + parts.push('withContribution'); + options.contribution = language.formatUnitList(contributionDescriptions); + } + + if (parts.length === 1) { + return entry; + } + + return language.formatString(parts.join('.'), options); + }; + + const addAccentsToAlbumLink = ({ + albumLink, + date, + duration, + entries, + }) => { + const options = {album: albumLink}; + const parts = ['artistPage.creditList.album']; + + if (date) { + parts.push('withDate'); + options.date = language.formatDate(new Date(date)); + } + + if (duration) { + parts.push('withDuration'); + options.duration = language.formatDuration(duration, { + approximate: entries.length > 1, + }); + } + + return language.formatString(parts.join('.'), options); + }; + + return relations.layout + .slots({ + title: data.name, + headingMode: 'sticky', + + mainClasses: ['long-content'], + mainContent: [ + sec.contextNotes && [ + html.tag('p', language.$('releaseInfo.note')), + html.tag('blockquote', + sec.contextNotes.content), + ], + + sec.visit && + html.tag('p', + language.$('releaseInfo.visitOn', { + links: language.formatDisjunctionList(sec.visit.externalLinks), + })), + + sec.artworks?.artistGalleryLink && + html.tag('p', + language.$('artistPage.viewArtGallery', { + link: sec.artworks.artistGalleryLink.slots({ + content: language.$('artistPage.viewArtGallery.link'), + }), + })), + + (sec.tracks || sec.artworsk || sec.flashes || sec.commentary) && + html.tag('p', + language.$('misc.jumpTo.withLinks', { + links: language.formatUnitList( + [ + sec.tracks && + html.tag('a', + {href: '#tracks'}, + language.$('artistPage.trackList.title')), + + sec.artworks && + html.tag('a', + {href: '#art'}, + language.$('artistPage.artList.title')), + + sec.flashes && + html.tag('a', + {href: '#flashes'}, + language.$('artistPage.flashList.title')), + + sec.commentary && + html.tag('a', + {href: '#commentary'}, + language.$('artistPage.commentaryList.title')), + ].filter(Boolean)), + })), + + sec.tracks && [ + sec.tracks.heading + .slots({ + tag: 'h2', + id: 'tracks', + title: language.$('artistPage.trackList.title'), + }), + + data.totalDuration > 0 && + html.tag('p', + language.$('artistPage.contributedDurationLine', { + artist: data.name, + duration: + language.formatDuration(data.totalDuration, { + approximate: data.totalTrackCount > 1, + unit: true, + }), + })), + + sec.tracks.groupInfo && + html.tag('p', + language.$('artistPage.musicGroupsLine', { + groups: + language.formatUnitList( + sec.tracks.groupInfo.map(({groupLink, count, duration}) => + (duration + ? language.$('artistPage.groupsLine.item.withDuration', { + group: groupLink, + duration: language.formatDuration(duration, {approximate: count > 1}), + }) + : language.$('artistPage.groupsLine.item.withCount', { + group: groupLink, + count: language.countContributions(count), + })))), + })), + + html.tag('dl', + sec.tracks.chunks.map(({albumLink, date, duration, entries}) => [ + html.tag('dt', + addAccentsToAlbumLink({albumLink, date, duration, entries})), + + html.tag('dd', + html.tag('ul', + entries + .map(({trackLink, duration, ...properties}) => ({ + entry: + (duration + ? language.$('artistPage.creditList.entry.track.withDuration', { + track: trackLink, + duration: language.formatDuration(duration), + }) + : language.$('artistPage.creditList.entry.track', { + track: trackLink, + })), + ...properties, + })) + .map(addAccentsToEntry) + .map(entry => html.tag('li', entry)))), + ])), + ], + + sec.artworks && [ + sec.artworks.heading + .slots({ + tag: 'h2', + id: 'art', + title: language.$('artistPage.artList.title'), + }), + + sec.artworks.artistGalleryLink && + html.tag('p', + language.$('artistPage.viewArtGallery.orBrowseList', { + link: sec.artworks.artistGalleryLink.slots({ + content: language.$('artistPage.viewArtGallery.link'), + }), + })), + + sec.artworks.groupInfo && + html.tag('p', + language.$('artistPage.artGroupsLine', { + groups: + language.formatUnitList( + sec.artworks.groupInfo.map(({groupLink, count}) => + language.$('artistPage.groupsLine.item.withCount', { + group: groupLink, + count: + language.countContributions(count), + }))), + })), + + html.tag('dl', + sec.artworks.chunks.map(({albumLink, date, entries}) => [ + html.tag('dt', + addAccentsToAlbumLink({albumLink, date, entries})), + + html.tag('dd', + html.tag('ul', + entries + .map(({kind, trackLink, ...properties}) => ({ + entry: + (kind === 'trackCover' + ? language.$('artistPage.creditList.entry.track', { + track: trackLink, + }) + : html.tag('i', + language.$('artistPage.creditList.entry.album.' + { + albumWallpaper: 'wallpaperArt', + albumBanner: 'bannerArt', + albumCover: 'coverArt', + }[kind]))), + ...properties, + })) + .map(addAccentsToEntry) + .map(entry => html.tag('li', entry)))), + ])), + ], + + sec.commentary && [ + sec.commentary.heading + .slots({ + tag: 'h2', + id: 'commentary', + title: language.$('artistPage.commentaryList.title'), + }), + + html.tag('dl', + sec.commentary.chunks.map(({albumLink, entries}) => [ + html.tag('dt', + language.$('artistPage.creditList.album', { + album: albumLink, + })), + + html.tag('dd', + html.tag('ul', + entries + .map(({kind, trackLink}) => + (kind === 'trackCommentary' + ? language.$('artistPage.creditList.entry.track', { + track: trackLink, + }) + : html.tag('i', + language.$('artistPage.creditList.entry.album.commentary')))) + .map(entry => html.tag('li', entry)))), + ])), + ], + ], + + navLinkStyle: 'hierarchical', + navLinks: + relations.artistNavLinks + .slots({ + showExtraLinks: true, + }) + .content, + }); + }, +}; + +/* + +export function write(artist, {wikiData}) { + const {groupData, wikiInfo} = wikiData; + + let flashes, flashListChunks; + if (wikiInfo.enableFlashesAndGames) { + flashes = sortFlashesChronologically(artist.flashesAsContributor.slice()); + flashListChunks = chunkByProperties( + flashes.map((flash) => ({ + act: flash.act, + flash, + date: flash.date, + // Manual artists/contrib properties here, 8ecause we don't + // want to show the full list of other contri8utors inline. + // (It can often 8e very, very large!) + artists: [], + contrib: flash.contributorContribs.find(({who}) => who === artist), + })), + ['act'] + ).map(({act, chunk}) => ({ + act, + chunk, + dateFirst: chunk[0].date, + dateLast: chunk[chunk.length - 1].date, + })); + } + + const unbound_serializeArtistsAndContrib = + (key, {serializeContribs, serializeLink}) => + (thing) => { + const {artists, contrib} = getArtistsAndContrib(thing, key); + const ret = {}; + ret.link = serializeLink(thing); + if (contrib.what) ret.contribution = contrib.what; + if (!empty(artists)) ret.otherArtists = serializeContribs(artists); + return ret; + }; + + const unbound_serializeTrackListChunks = (chunks, {serializeLink}) => + chunks.map(({date, album, chunk, duration}) => ({ + album: serializeLink(album), + date, + duration, + tracks: chunk.map(({track}) => ({ + link: serializeLink(track), + duration: track.duration, + })), + })); + + const jumpTo = { + tracks: !empty(allTracks), + art: !empty(artThingsAll), + flashes: wikiInfo.enableFlashesAndGames && !empty(flashes), + commentary: !empty(commentaryThings), + }; + + const showJumpTo = Object.values(jumpTo).includes(true); + + const data = { + type: 'data', + path: ['artist', artist.directory], + data: ({serializeContribs, serializeLink}) => { + const serializeArtistsAndContrib = bindOpts(unbound_serializeArtistsAndContrib, { + serializeContribs, + serializeLink, + }); + + const serializeTrackListChunks = bindOpts(unbound_serializeTrackListChunks, { + serializeLink, + }); + + return { + albums: { + asCoverArtist: artist.albumsAsCoverArtist + .map(serializeArtistsAndContrib('coverArtistContribs')), + asWallpaperArtist: artist.albumsAsWallpaperArtist + .map(serializeArtistsAndContrib('wallpaperArtistContribs')), + asBannerArtist: artist.albumsAsBannerArtis + .map(serializeArtistsAndContrib('bannerArtistContribs')), + }, + flashes: wikiInfo.enableFlashesAndGames + ? { + asContributor: artist.flashesAsContributor + .map(flash => getArtistsAndContrib(flash, 'contributorContribs')) + .map(({contrib, thing: flash}) => ({ + link: serializeLink(flash), + contribution: contrib.what, + })), + } + : null, + tracks: { + asArtist: artist.tracksAsArtist + .map(serializeArtistsAndContrib('artistContribs')), + asContributor: artist.tracksAsContributo + .map(serializeArtistsAndContrib('contributorContribs')), + chunked: serializeTrackListChunks(trackListChunks), + }, + }; + }, + }; + + const infoPage = { + type: 'page', + path: ['artist', artist.directory], + page: ({ + fancifyURL, + generateInfoGalleryLinks, + getArtistAvatar, + getArtistString, + html, + link, + language, + transformMultiline, + }) => { + const generateTrackList = bindOpts(unbound_generateTrackList, { + getArtistString, + html, + language, + link, + }); + + return { + title: language.$('artistPage.title', {artist: name}), + + cover: artist.hasAvatar && { + src: getArtistAvatar(artist), + alt: language.$('misc.alt.artistAvatar'), + }, + + main: { + headingMode: 'sticky', + + content: [ + ...html.fragment( + wikiInfo.enableFlashesAndGames && + !empty(flashes) && [ + html.tag('h2', + {id: 'flashes', class: ['content-heading']}, + language.$('artistPage.flashList.title')), + + html.tag('dl', + flashListChunks.flatMap(({ + act, + chunk, + dateFirst, + dateLast, + }) => [ + html.tag('dt', + language.$('artistPage.creditList.flashAct.withDateRange', { + act: link.flash(chunk[0].flash, { + text: act.name, + }), + dateRange: language.formatDateRange( + dateFirst, + dateLast + ), + })), + + html.tag('dd', + html.tag('ul', + chunk + .map(({flash, ...props}) => ({ + ...props, + entry: language.$('artistPage.creditList.entry.flash', { + flash: link.flash(flash), + }), + })) + .map(opts => generateEntryAccents({ + getArtistString, + language, + ...opts, + })) + .map(row => html.tag('li', row)))), + ])), + ]), + ], + }, + }; + }, + }; + + const artThingsGallery = sortAlbumsTracksChronologically( + [ + ...(artist.albumsAsCoverArtist ?? []), + ...(artist.tracksAsCoverArtist ?? []), + ], + {latestFirst: true, getDate: (o) => o.coverArtDate}); + + const galleryPage = hasGallery && { + type: 'page', + path: ['artistGallery', artist.directory], + page: ({ + generateInfoGalleryLinks, + getAlbumCover, + getGridHTML, + getTrackCover, + html, + link, + language, + }) => ({ + title: language.$('artistGalleryPage.title', {artist: name}), + + main: { + classes: ['top-index'], + headingMode: 'static', + + content: [ + html.tag('p', + {class: 'quick-info'}, + language.$('artistGalleryPage.infoLine', { + coverArts: language.countCoverArts(artThingsGallery.length, { + unit: true, + }), + })), + + html.tag('div', + {class: 'grid-listing'}, + getGridHTML({ + entries: artThingsGallery.map((item) => ({item})), + srcFn: (thing) => + thing.album + ? getTrackCover(thing) + : getAlbumCover(thing), + linkFn: (thing, opts) => + thing.album + ? link.track(thing, opts) + : link.album(thing, opts), + })), + ], + }, + }), + }; + + return [data, infoPage, galleryPage].filter(Boolean); +} + +*/ diff --git a/src/content/dependencies/generateArtistNavLinks.js b/src/content/dependencies/generateArtistNavLinks.js new file mode 100644 index 00000000..f283b30d --- /dev/null +++ b/src/content/dependencies/generateArtistNavLinks.js @@ -0,0 +1,105 @@ +import {empty} from '../../util/sugar.js'; + +export default { + contentDependencies: [ + 'linkArtist', + 'linkArtistGallery', + ], + + extraDependencies: ['html', 'language', 'wikiData'], + + sprawl({wikiInfo}) { + return { + enableListings: wikiInfo.enableListings, + }; + }, + + relations(relation, sprawl, artist) { + const relations = {}; + + relations.artistMainLink = + relation('linkArtist', artist); + + relations.artistInfoLink = + relation('linkArtist', artist); + + if ( + !empty(artist.albumsAsCoverArtist) || + !empty(artist.tracksAsCoverArtist) + ) { + relations.artistGalleryLink = + relation('linkArtistGallery', artist); + } + + return relations; + }, + + data(sprawl) { + return { + enableListings: sprawl.enableListings, + }; + }, + + generate(data, relations, {html, language}) { + return html.template({ + annotation: `generateArtistNav`, + slots: { + showExtraLinks: {type: 'boolean', default: false}, + + currentExtra: { + validate: v => v.is('gallery'), + }, + }, + + content(slots) { + const infoLink = + relations.artistInfoLink?.slots({ + attributes: {class: slots.currentExtra === null && 'current'}, + content: language.$('misc.nav.info'), + }); + + const {content: extraLinks = []} = + slots.showExtraLinks && + {content: [ + relations.artistGalleryLink?.slots({ + attributes: {class: slots.currentExtra === 'gallery' && 'current'}, + content: language.$('misc.nav.gallery'), + }), + ]}; + + const mostAccentLinks = [ + ...extraLinks, + ].filter(Boolean); + + // Don't show the info accent link all on its own. + const allAccentLinks = + (empty(mostAccentLinks) + ? [] + : [infoLink, ...mostAccentLinks]); + + const accent = + (empty(allAccentLinks) + ? html.blank() + : `(${language.formatUnitList(allAccentLinks)})`); + + return [ + {auto: 'home'}, + + data.enableListings && + { + path: ['localized.listingIndex'], + title: language.$('listingIndex.title'), + }, + + { + accent, + html: + language.$('artistPage.nav.artist', { + artist: relations.artistMainLink, + }), + }, + ]; + }, + }); + }, +}; diff --git a/src/content/dependencies/generateChronologyLinks.js b/src/content/dependencies/generateChronologyLinks.js new file mode 100644 index 00000000..a61b5e6f --- /dev/null +++ b/src/content/dependencies/generateChronologyLinks.js @@ -0,0 +1,88 @@ +import {accumulateSum, empty} from '../../util/sugar.js'; + +export default { + extraDependencies: ['html', 'language'], + + generate({html, language}) { + return html.template({ + annotation: `generateChronologyLinks`, + + slots: { + chronologyInfoSets: { + validate: v => + v.arrayOf( + v.validateProperties({ + headingString: v.isString, + contributions: v.arrayOf(v.validateProperties({ + index: v.isCountingNumber, + artistLink: v.isHTML, + previousLink: v.isHTML, + nextLink: v.isHTML, + })), + })), + } + }, + + content(slots) { + if (empty(slots.chronologyInfoSets)) { + return html.blank(); + } + + const totalContributionCount = + accumulateSum( + slots.chronologyInfoSets, + ({contributions}) => contributions.length); + + if (totalContributionCount === 0) { + return html.blank(); + } + + if (totalContributionCount > 8) { + return html.tag('div', {class: 'chronology'}, + language.$('misc.chronology.seeArtistPages')); + } + + return html.tags( + slots.chronologyInfoSets.map(({ + headingString, + contributions, + }) => + contributions.map(({ + index, + artistLink, + previousLink, + nextLink, + }) => { + const heading = + html.tag('span', {class: 'heading'}, + language.$(headingString, { + index: language.formatIndex(index), + artist: artistLink, + })); + + const navigation = + (previousLink || nextLink) && + html.tag('span', {class: 'buttons'}, + language.formatUnitList([ + previousLink?.slots({ + tooltip: true, + color: false, + content: language.$('misc.nav.previous'), + }), + + nextLink?.slots({ + tooltip: true, + color: false, + content: language.$('misc.nav.next'), + }), + ].filter(Boolean))); + + return html.tag('div', {class: 'chronology'}, + (navigation + ? language.$('misc.chronology.withNavigation', {heading, navigation}) + : heading)); + }))); + }, + }); + }, +}; diff --git a/src/content/dependencies/generateColorStyleRules.js b/src/content/dependencies/generateColorStyleRules.js new file mode 100644 index 00000000..44600935 --- /dev/null +++ b/src/content/dependencies/generateColorStyleRules.js @@ -0,0 +1,41 @@ +export default { + extraDependencies: [ + 'getColors', + ], + + data(color) { + return {color}; + }, + + generate(data, { + getColors, + }) { + if (!data.color) return ''; + + const { + primary, + dark, + dim, + dimGhost, + bg, + bgBlack, + shadow, + } = getColors(data.color); + + const variables = [ + `--primary-color: ${primary}`, + `--dark-color: ${dark}`, + `--dim-color: ${dim}`, + `--dim-ghost-color: ${dimGhost}`, + `--bg-color: ${bg}`, + `--bg-black-color: ${bgBlack}`, + `--shadow-color: ${shadow}`, + ]; + + return [ + `:root {`, + ...variables.map((line) => ` ${line};`), + `}`, + ].join('\n'); + }, +}; diff --git a/src/content/dependencies/generateContentHeading.js b/src/content/dependencies/generateContentHeading.js new file mode 100644 index 00000000..1666ef4b --- /dev/null +++ b/src/content/dependencies/generateContentHeading.js @@ -0,0 +1,27 @@ +export default { + extraDependencies: [ + 'html', + ], + + generate({html}) { + return html.template({ + annotation: 'generateContentHeading', + + slots: { + title: {type: 'html'}, + id: {type: 'string'}, + tag: {type: 'string', default: 'p'}, + }, + + content(slots) { + return html.tag(slots.tag, + { + class: 'content-heading', + id: slots.id, + tabindex: '0', + }, + slots.title); + }, + }); + } +} diff --git a/src/content/dependencies/generateCoverArtwork.js b/src/content/dependencies/generateCoverArtwork.js new file mode 100644 index 00000000..a7a7f859 --- /dev/null +++ b/src/content/dependencies/generateCoverArtwork.js @@ -0,0 +1,83 @@ +import {empty} from '../../util/sugar.js'; + +export default { + contentDependencies: ['image', 'linkArtTag'], + extraDependencies: ['html', 'language'], + + relations(relation, artTags) { + const relations = {}; + + relations.image = + relation('image', artTags); + + if (artTags) { + relations.tagLinks = + artTags + .filter(tag => !tag.isContentWarning) + .map(tag => relation('linkArtTag', tag)); + } else { + relations.tagLinks = null; + } + + return relations; + }, + + generate(relations, {html, language}) { + return html.template({ + annotation: 'generateCoverArtwork', + + slots: { + path: { + validate: v => v.validateArrayItems(v.isString), + }, + + alt: { + type: 'string', + }, + + displayMode: { + validate: v => v.is('primary', 'thumbnail'), + default: 'primary', + }, + }, + + content(slots) { + switch (slots.displayMode) { + case 'primary': + return html.tag('div', {id: 'cover-art-container'}, [ + relations.image + .slots({ + path: slots.path, + alt: slots.alt, + thumb: 'medium', + id: 'cover-art', + reveal: true, + link: true, + square: true, + }), + + !empty(relations.tagLinks) && + html.tag('p', + language.$('releaseInfo.artTags.inline', { + tags: language.formatUnitList(relations.tagLinks), + })), + ]); + + case 'thumbnail': + return relations.image + .slots({ + path: slots.path, + alt: slots.alt, + thumb: 'small', + reveal: false, + link: false, + square: true, + }); + + case 'default': + return html.blank(); + } + }, + }); + }, +}; diff --git a/src/content/dependencies/generateFooterLocalizationLinks.js b/src/content/dependencies/generateFooterLocalizationLinks.js new file mode 100644 index 00000000..01b5b209 --- /dev/null +++ b/src/content/dependencies/generateFooterLocalizationLinks.js @@ -0,0 +1,56 @@ +export default { + extraDependencies: [ + 'defaultLanguage', + 'html', + 'language', + 'languages', + 'pagePath', + 'to', + ], + + generate({ + defaultLanguage, + html, + language, + languages, + pagePath, + to, + }) { + const links = Object.entries(languages) + .filter(([code, language]) => code !== 'default' && !language.hidden) + .map(([code, language]) => language) + .sort(({name: a}, {name: b}) => (a < b ? -1 : a > b ? 1 : 0)) + .map((language) => + html.tag('span', + html.tag('a', + { + href: + language === defaultLanguage + ? to( + 'localizedDefaultLanguage.' + pagePath[0], + ...pagePath.slice(1)) + : to( + 'localizedWithBaseDirectory.' + pagePath[0], + language.code, + ...pagePath.slice(1)), + }, + language.name))); + + return html.tag('div', {class: 'footer-localization-links'}, + language.$('misc.uiLanguage', { + languages: links.join('\n'), + })); + }, +}; + +/* +function unbound_getFooterLocalizationLinks({ + html, + defaultLanguage, + language, + languages, + pagePath, + to, +}) { +} +*/ diff --git a/src/content/dependencies/generatePageLayout.js b/src/content/dependencies/generatePageLayout.js new file mode 100644 index 00000000..55f5b940 --- /dev/null +++ b/src/content/dependencies/generatePageLayout.js @@ -0,0 +1,506 @@ +import {empty, openAggregate} from '../../util/sugar.js'; + +export default { + contentDependencies: [ + 'generateColorStyleRules', + 'generateFooterLocalizationLinks', + 'generateStickyHeadingContainer', + 'transformContent', + ], + + extraDependencies: [ + 'cachebust', + 'html', + 'language', + 'to', + 'transformMultiline', + 'wikiData', + ], + + sprawl({wikiInfo}) { + return { + footerContent: wikiInfo.footerContent, + wikiColor: wikiInfo.color, + wikiName: wikiInfo.nameShort, + }; + }, + + data({wikiName}) { + return { + wikiName, + }; + }, + + relations(relation, sprawl) { + const relations = {}; + + relations.footerLocalizationLinks = + relation('generateFooterLocalizationLinks'); + + relations.stickyHeadingContainer = + relation('generateStickyHeadingContainer'); + + relations.defaultFooterContent = + relation('transformContent', sprawl.footerContent); + + relations.defaultColorStyleRules = + relation('generateColorStyleRules', sprawl.wikiColor); + + return relations; + }, + + generate(data, relations, { + cachebust, + html, + language, + to, + }) { + const sidebarSlots = side => ({ + // Content is a flat HTML array. It'll generate one sidebar section + // if specified. + [side + 'Content']: {type: 'html'}, + + // Multiple is an array of {content: (HTML)} objects. Each of these + // will generate one sidebar section. + [side + 'Multiple']: { + validate: v => + v.arrayOf( + v.validateProperties({ + content: v.isHTML, + })), + }, + + // Sticky mode controls which sidebar section(s), if any, follow the + // scroll position, "sticking" to the top of the browser viewport. + // + // 'last' - last or only sidebar box is sticky + // 'column' - entire column, incl. multiple boxes from top, is sticky + // 'none' - sidebar not sticky at all, stays at top of page + // + // Note: This doesn't affect the content of any sidebar section, only + // the whole section's containing box (or the sidebar column as a whole). + [side + 'StickyMode']: { + validate: v => v.is('last', 'column', 'static'), + }, + + // Collapsing sidebars disappear when the viewport is sufficiently + // thin. (This is the default.) Override as false to make the sidebar + // stay visible in thinner viewports, where the page layout will be + // reflowed so the sidebar is as wide as the screen and appears below + // nav, above the main content. + [side + 'Collapse']: {type: 'boolean', default: true}, + + // Wide sidebars generally take up more horizontal space in the normal + // page layout, and should be used if the content of the sidebar has + // a greater than typical focus compared to main content. + [side + 'Wide']: {type: 'boolean', defualt: false}, + }); + + return html.template({ + annotation: 'generatePageLayout', + + slots: { + title: {type: 'html'}, + cover: {type: 'html'}, + coverNeedsReveal: {type: 'boolean'}, + + socialEmbed: {type: 'html'}, + + colorStyleRules: { + validate: v => v.arrayOf(v.isString), + default: [], + }, + + additionalStyleRules: { + validate: v => v.arrayOf(v.isString), + default: [], + }, + + mainClasses: { + validate: v => v.arrayOf(v.isString), + default: [], + }, + + // Main + + mainContent: {type: 'html'}, + + headingMode: { + validate: v => v.is('sticky', 'static'), + default: 'static', + }, + + // Sidebars + + ...sidebarSlots('leftSidebar'), + ...sidebarSlots('rightSidebar'), + + // Nav & Footer + + navContent: {type: 'html'}, + navBottomRowContent: {type: 'html'}, + + navLinkStyle: { + validate: v => v.is('hierarchical', 'index'), + default: 'index', + }, + + navLinks: { + validate: v => + v.arrayOf(object => { + v.isObject(object); + + const aggregate = openAggregate({message: `Errors validating navigation link`}); + + aggregate.call(v.validateProperties({ + auto: () => true, + html: () => true, + + path: () => true, + title: () => true, + accent: () => true, + }), object); + + if (object.auto || object.html) { + if (object.auto && object.html) { + aggregate.push(new TypeError(`Don't specify both auto and html`)); + } else if (object.auto) { + aggregate.call(v.is('home', 'current'), object.auto); + } else { + aggregate.call(v.isHTML, object.html); + } + + if (object.path || object.title) { + aggregate.push(new TypeError(`Don't specify path or title along with auto or html`)); + } + } else { + aggregate.call(v.validateProperties({ + path: v.arrayOf(v.isString), + title: v.isString, + }), { + path: object.path, + title: object.title, + }); + } + + aggregate.close(); + + return true; + }) + }, + + footerContent: {type: 'html'}, + }, + + content(slots) { + let titleHTML = null; + + if (!html.isBlank(slots.title)) { + switch (slots.headingMode) { + case 'sticky': + titleHTML = + relations.stickyHeadingContainer.slots({ + title: slots.title, + cover: slots.cover, + needsReveal: slots.coverNeedsReveal, + }); + break; + case 'static': + titleHTML = html.tag('h1', slots.title); + break; + } + } + + let footerContent = slots.footerContent; + + if (html.isBlank(footerContent)) { + footerContent = relations.defaultFooterContent + .slot('mode', 'multiline'); + } + + const mainHTML = + html.tag('main', { + id: 'content', + class: slots.mainClasses, + }, [ + titleHTML, + + slots.cover, + + html.tag('div', + { + [html.onlyIfContent]: true, + class: 'main-content-container', + }, + slots.mainContent), + ]); + + const footerHTML = + html.tag('footer', + {[html.onlyIfContent]: true, id: 'footer'}, + [ + html.tag('div', + { + [html.onlyIfContent]: true, + class: 'footer-content', + }, + footerContent), + + relations.footerLocalizationLinks, + ]); + + const navHTML = html.tag('nav', + { + [html.onlyIfContent]: true, + id: 'header', + class: [ + !empty(slots.navLinks) && 'nav-has-main-links', + !html.isBlank(slots.navContent) && 'nav-has-content', + !html.isBlank(slots.navBottomRowContent) && 'nav-has-bottom-row', + ], + }, + [ + html.tag('div', + { + [html.onlyIfContent]: true, + class: [ + 'nav-main-links', + 'nav-links-' + slots.navLinkStyle, + ], + }, + slots.navLinks?.map((cur, i) => { + let content; + + if (cur.html) { + content = cur.html; + } else { + let title; + let href; + + switch (cur.auto) { + case 'home': + title = data.wikiName; + href = to('localized.home'); + break; + case 'current': + title = slots.title; + href = ''; + break; + case null: + case undefined: + title = cur.title; + href = to(...cur.path); + break; + } + + content = html.tag('a', + {href}, + title); + } + + let className; + + if (cur.auto === 'current') { + className = 'current'; + } else if ( + slots.navLinkStyle === 'hierarchical' && + i === slots.navLinks.length - 1 + ) { + className = 'current'; + } + + return html.tag('span', + {class: className}, + [ + html.tag('span', + {class: 'nav-link-content'}, + content), + html.tag('span', + {[html.onlyIfContent]: true, class: 'nav-link-accent'}, + cur.accent), + ]); + })), + + html.tag('div', + {[html.onlyIfContent]: true, class: 'nav-bottom-row'}, + slots.navBottomRowContent), + + html.tag('div', + {[html.onlyIfContent]: true, class: 'nav-content'}, + slots.navContent), + ]) + + const generateSidebarHTML = (side, id) => { + const content = slots[side + 'Content']; + const multiple = slots[side + 'Multiple']; + const stickyMode = slots[side + 'StickyMode']; + const wide = slots[side + 'Wide']; + const collapse = slots[side + 'Collapse']; + + let sidebarClasses = []; + let sidebarContent = html.blank(); + + if (!html.isBlank(content)) { + sidebarClasses = ['sidebar']; + sidebarContent = content; + } else if (multiple) { + sidebarClasses = ['sidebar-multiple']; + sidebarContent = + multiple + .filter(Boolean) + .map(({content}) => + html.tag('div', + { + [html.onlyIfContent]: true, + class: 'sidebar', + }, + content)); + } + + return html.tag('div', + { + [html.onlyIfContent]: true, + id, + class: [ + 'sidebar-column', + wide && 'wide', + !collapse && 'no-hide', + stickyMode !== 'static' && `sticky-${stickyMode}`, + ...sidebarClasses, + ], + }, + sidebarContent); + } + + const sidebarLeftHTML = generateSidebarHTML('leftSidebar', 'sidebar-left'); + const sidebarRightHTML = generateSidebarHTML('rightSidebar', 'sidebar-right'); + const collapseSidebars = slots.leftSidebarCollapse && slots.rightSidebarCollapse; + + const layoutHTML = [ + navHTML, + // banner.position === 'top' && bannerHTML, + // secondaryNavHTML, + html.tag('div', + { + class: [ + 'layout-columns', + !collapseSidebars && 'vertical-when-thin', + (sidebarLeftHTML || sidebarRightHTML) && 'has-one-sidebar', + (sidebarLeftHTML && sidebarRightHTML) && 'has-two-sidebars', + !(sidebarLeftHTML || sidebarRightHTML) && 'has-zero-sidebars', + sidebarLeftHTML && 'has-sidebar-left', + sidebarRightHTML && 'has-sidebar-right', + ], + }, + [ + sidebarLeftHTML, + mainHTML, + sidebarRightHTML, + ]), + // banner.position === 'bottom' && bannerHTML, + footerHTML, + ].filter(Boolean).join('\n'); + + const documentHTML = html.tags([ + `<!DOCTYPE html>`, + html.tag('html', + { + lang: language.intlCode, + 'data-language-code': language.code, + + /* + 'data-url-key': 'localized.' + pagePath[0], + ...Object.fromEntries( + pagePath.slice(1).map((v, i) => [['data-url-value' + i], v])), + */ + + 'data-rebase-localized': to('localized.root'), + 'data-rebase-shared': to('shared.root'), + 'data-rebase-media': to('media.root'), + 'data-rebase-data': to('data.root'), + }, + [ + // developersComment, + + html.tag('head', [ + /* + html.tag('title', + showWikiNameInTitle + ? language.formatString('misc.pageTitle.withWikiName', { + title, + wikiName: data.wikiName, + }) + : language.formatString('misc.pageTitle', {title})), + */ + + html.tag('meta', {charset: 'utf-8'}), + html.tag('meta', { + name: 'viewport', + content: 'width=device-width, initial-scale=1', + }), + + /* + ...( + Object.entries(meta) + .filter(([key, value]) => value) + .map(([key, value]) => html.tag('meta', {[key]: value}))), + + canonical && + html.tag('link', { + rel: 'canonical', + href: canonical, + }), + + ...( + localizedCanonical + .map(({lang, href}) => html.tag('link', { + rel: 'alternate', + hreflang: lang, + href, + }))), + + */ + + // slots.socialEmbed, + + html.tag('link', { + rel: 'stylesheet', + href: to('shared.staticFile', `site4.css?${cachebust}`), + }), + + html.tag('style', [ + (empty(slots.colorStyleRules) + ? relations.defaultColorStyleRules + : slots.colorStyleRules), + slots.additionalStyleRules, + ]), + + html.tag('script', { + src: to('shared.staticFile', `lazy-loading.js?${cachebust}`), + }), + ]), + + html.tag('body', + // {style: body.style || ''}, + [ + html.tag('div', {id: 'page-container'}, [ + // mainHTML && skippersHTML, + layoutHTML, + ]), + + // infoCardHTML, + // imageOverlayHTML, + + html.tag('script', { + type: 'module', + src: to('shared.staticFile', `client.js?${cachebust}`), + }), + ]), + ]) + ]); + + return documentHTML; + }, + }); + }, +}; diff --git a/src/content/dependencies/generatePreviousNextLinks.js b/src/content/dependencies/generatePreviousNextLinks.js new file mode 100644 index 00000000..42b2c42b --- /dev/null +++ b/src/content/dependencies/generatePreviousNextLinks.js @@ -0,0 +1,36 @@ +export default { + // Returns an array with the slotted previous and next links, prepared + // for inclusion in a page's navigation bar. Include with other links + // in the nav bar and then join them all as a unit list, for example. + + extraDependencies: ['html', 'language'], + + generate({html, language}) { + return html.template({ + annotation: `generatePreviousNextLinks`, + + slots: { + previousLink: {type: 'html'}, + nextLink: {type: 'html'}, + }, + + content(slots) { + return [ + !html.isBlank(slots.previousLink) && + slots.previousLink.slots({ + tooltip: true, + attributes: {id: 'previous-button'}, + content: language.$('misc.nav.previous'), + }), + + !html.isBlank(slots.nextLink) && + slots.nextLink?.slots({ + tooltip: true, + attributes: {id: 'next-button'}, + content: language.$('misc.nav.next'), + }), + ].filter(Boolean); + }, + }); + }, +}; diff --git a/src/content/dependencies/generateStaticPage.js b/src/content/dependencies/generateStaticPage.js new file mode 100644 index 00000000..cbd477e0 --- /dev/null +++ b/src/content/dependencies/generateStaticPage.js @@ -0,0 +1,39 @@ +export default { + contentDependencies: ['generatePageLayout', 'transformContent'], + + relations(relation, staticPage) { + return { + layout: relation('generatePageLayout'), + content: relation('transformContent', staticPage.content), + }; + }, + + data(staticPage) { + return { + name: staticPage.name, + stylesheet: staticPage.stylesheet, + }; + }, + + generate(data, relations) { + return relations.layout + .slots({ + title: data.name, + headingMode: 'sticky', + + additionalStyleRules: + (data.stylesheet + ? [data.stylesheet] + : []), + + mainClasses: ['long-content'], + mainContent: relations.content, + + navLinkStyle: 'hierarchical', + navLinks: [ + {auto: 'home'}, + {auto: 'current'}, + ], + }); + }, +}; diff --git a/src/content/dependencies/generateStickyHeadingContainer.js b/src/content/dependencies/generateStickyHeadingContainer.js new file mode 100644 index 00000000..fb6d8307 --- /dev/null +++ b/src/content/dependencies/generateStickyHeadingContainer.js @@ -0,0 +1,45 @@ +export default { + extraDependencies: ['html'], + + generate({html}) { + return html.template({ + annotation: `generateStickyHeadingContainer`, + + slots: { + title: {type: 'html'}, + cover: {type: 'html'}, + needsReveal: {type: 'boolean', default: false}, + }, + + content(slots) { + const hasCover = !html.isBlank(slots.cover); + + return html.tag('div', + { + class: [ + 'content-sticky-heading-container', + hasCover && 'has-cover', + ], + }, + [ + html.tag('div', {class: 'content-sticky-heading-row'}, [ + html.tag('h1', slots.title), + + hasCover && + html.tag('div', {class: 'content-sticky-heading-cover-container'}, + html.tag('div', + {class: [ + 'content-sticky-heading-cover', + slots.needsReveal && + 'content-sticky-heading-cover-needs-reveal', + ]}, + slots.cover.slot('displayMode', 'thumbnail'))) + ]), + + html.tag('div', {class: 'content-sticky-subheading-row'}, + html.tag('h2', {class: 'content-sticky-subheading'})), + ]); + }, + }); + }, +}; diff --git a/src/content/dependencies/generateTrackInfoPage.js b/src/content/dependencies/generateTrackInfoPage.js new file mode 100644 index 00000000..ee68f534 --- /dev/null +++ b/src/content/dependencies/generateTrackInfoPage.js @@ -0,0 +1,132 @@ +import getChronologyRelations from '../util/getChronologyRelations.js'; +import {sortAlbumsTracksChronologically} from '../../util/wiki-data.js'; + +export default { + contentDependencies: [ + 'generateTrackInfoPageContent', + 'generateAlbumNavAccent', + 'generateAlbumSidebar', + 'generateAlbumStyleRules', + 'generateChronologyLinks', + 'generateColorStyleRules', + 'generatePageLayout', + 'linkAlbum', + 'linkArtist', + 'linkTrack', + ], + + extraDependencies: ['language'], + + relations(relation, track) { + return { + layout: relation('generatePageLayout'), + + artistChronologyContributions: getChronologyRelations(track, { + contributions: [...track.artistContribs, ...track.contributorContribs], + + linkArtist: artist => relation('linkArtist', artist), + linkThing: track => relation('linkTrack', track), + + getThings: artist => + sortAlbumsTracksChronologically([ + ...artist.tracksAsArtist, + ...artist.tracksAsContributor, + ]), + }), + + coverArtistChronologyContributions: getChronologyRelations(track, { + contributions: track.coverArtistContribs, + + linkArtist: artist => relation('linkArtist', artist), + + linkThing: trackOrAlbum => + (trackOrAlbum.album + ? relation('linkTrack', trackOrAlbum) + : relation('linkAlbum', trackOrAlbum)), + + getThings: artist => + sortAlbumsTracksChronologically([ + ...artist.albumsAsCoverArtist, + ...artist.tracksAsCoverArtist, + ], { + getDate: albumOrTrack => albumOrTrack.coverArtDate, + }), + }), + + albumLink: relation('linkAlbum', track.album), + trackLink: relation('linkTrack', track), + albumNavAccent: relation('generateAlbumNavAccent', track.album, track), + chronologyLinks: relation('generateChronologyLinks'), + + content: relation('generateTrackInfoPageContent', track), + sidebar: relation('generateAlbumSidebar', track.album, track), + albumStyleRules: relation('generateAlbumStyleRules', track.album), + colorStyleRules: relation('generateColorStyleRules', track.color), + }; + }, + + data(track) { + return { + name: track.name, + + hasTrackNumbers: track.album.hasTrackNumbers, + trackNumber: track.album.tracks.indexOf(track) + 1, + }; + }, + + generate(data, relations, {language}) { + return relations.layout + .slots({ + title: language.$('trackPage.title', {track: data.name}), + headingMode: 'sticky', + + colorStyleRules: [relations.colorStyleRules], + additionalStyleRules: [relations.albumStyleRules], + + cover: relations.content.cover, + coverNeedsReveal: relations.content.coverNeedsReveal, + mainContent: relations.content.main.content, + + navLinkStyle: 'hierarchical', + navLinks: [ + {auto: 'home'}, + {html: relations.albumLink}, + { + html: + (data.hasTrackNumbers + ? language.$('trackPage.nav.track.withNumber', { + number: data.trackNumber, + track: relations.trackLink + .slot('attributes', {class: 'current'}), + }) + : language.$('trackPage.nav.track', { + track: relations.trackLink + .slot('attributes', {class: 'current'}), + })), + }, + ], + + navBottomRowContent: + relations.albumNavAccent.slots({ + showTrackNavigation: true, + showExtraLinks: false, + }), + + navContent: + relations.chronologyLinks.slots({ + chronologyInfoSets: [ + { + headingString: 'misc.chronology.heading.track', + contributions: relations.artistChronologyContributions, + }, + { + headingString: 'misc.chronology.heading.coverArt', + contributions: relations.coverArtistChronologyContributions, + }, + ], + }), + + ...relations.sidebar, + }); + }, +} diff --git a/src/content/dependencies/generateTrackInfoPageContent.js b/src/content/dependencies/generateTrackInfoPageContent.js new file mode 100644 index 00000000..c33c2f62 --- /dev/null +++ b/src/content/dependencies/generateTrackInfoPageContent.js @@ -0,0 +1,671 @@ +import {empty} from '../../util/sugar.js'; +import {sortFlashesChronologically} from '../../util/wiki-data.js'; + +export default { + contentDependencies: [ + 'generateAdditionalFilesShortcut', + 'generateAlbumAdditionalFilesList', + 'generateContentHeading', + 'generateCoverArtwork', + 'generateTrackList', + 'generateTrackListDividedByGroups', + 'linkAlbum', + 'linkContribution', + 'linkExternal', + 'linkFlash', + 'linkTrack', + 'transformContent', + ], + + extraDependencies: ['html', 'language', 'wikiData'], + + sprawl({wikiInfo}) { + return { + divideTrackListsByGroups: wikiInfo.divideTrackListsByGroups, + enableFlashesAndGames: wikiInfo.enableFlashesAndGames, + }; + }, + + relations(relation, sprawl, track) { + const {album} = track; + + const relations = {}; + const sections = relations.sections = {}; + + const contributionLinksRelation = contribs => + contribs + .slice(0, 4) + .map(contrib => + relation('linkContribution', contrib.who, contrib.what)); + + const additionalFilesSection = additionalFiles => ({ + heading: relation('generateContentHeading'), + list: relation('generateAlbumAdditionalFilesList', album, additionalFiles), + }); + + // Section: Release info + + const releaseInfo = sections.releaseInfo = {}; + + releaseInfo.artistContributionLinks = + contributionLinksRelation(track.artistContribs); + + if (track.hasUniqueCoverArt) { + relations.cover = + relation('generateCoverArtwork', track.artTags); + releaseInfo.coverArtistContributionLinks = + contributionLinksRelation(track.coverArtistContribs); + } else if (album.hasCoverArt) { + relations.cover = + relation('generateCoverArtwork', album.artTags); + } else { + relations.cover = null; + } + + // Section: Listen on + + const listen = sections.listen = {}; + + if (!empty(track.urls)) { + listen.externalLinks = + track.urls.map(url => + relation('linkExternal', url)); + } + + // Section: Extra links + + const extra = sections.extra = {}; + + if (!empty(track.additionalFiles)) { + extra.additionalFilesShortcut = + relation('generateAdditionalFilesShortcut', track.additionalFiles); + } + + // Section: Other releases + + if (!empty(track.otherReleases)) { + const otherReleases = sections.otherReleases = {}; + + otherReleases.heading = + relation('generateContentHeading'); + + otherReleases.items = + track.otherReleases.map(track => ({ + trackLink: relation('linkTrack', track), + albumLink: relation('linkAlbum', track.album), + })); + } + + // Section: Contributors + + if (!empty(track.contributorContribs)) { + const contributors = sections.contributors = {}; + + contributors.heading = + relation('generateContentHeading'); + + contributors.contributionLinks = + contributionLinksRelation(track.contributorContribs); + } + + // Section: Referenced tracks + + if (!empty(track.referencedTracks)) { + const references = sections.references = {}; + + references.heading = + relation('generateContentHeading'); + + references.list = + relation('generateTrackList', track.referencedTracks); + } + + // Section: Tracks that reference + + if (!empty(track.referencedByTracks)) { + const referencedBy = sections.referencedBy = {}; + + referencedBy.heading = + relation('generateContentHeading'); + + referencedBy.list = + relation('generateTrackListDividedByGroups', + track.referencedByTracks, + sprawl.divideTrackListsByGroups); + } + + // Section: Sampled tracks + + if (!empty(track.sampledTracks)) { + const samples = sections.samples = {}; + + samples.heading = + relation('generateContentHeading'); + + samples.list = + relation('generateTrackList', track.sampledTracks); + } + + // Section: Tracks that sample + + if (!empty(track.sampledByTracks)) { + const sampledBy = sections.sampledBy = {}; + + sampledBy.heading = + relation('generateContentHeading'); + + sampledBy.list = + relation('generateTrackListDividedByGroups', + track.sampledByTracks, + sprawl.divideTrackListsByGroups); + } + + // Section: Flashes that feature + + if (sprawl.enableFlashesAndGames) { + const sortedFeatures = + sortFlashesChronologically( + [track, ...track.otherReleases].flatMap(track => + track.featuredInFlashes.map(flash => ({ + // These aren't going to be exposed directly, they're processed + // into the appropriate relations after this sort. + flash, track, + + // These properties are only used for the sort. + act: flash.act, + date: flash.date, + })))); + + if (!empty(sortedFeatures)) { + const flashesThatFeature = sections.flashesThatFeature = {}; + + flashesThatFeature.heading = + relation('generateContentHeading'); + + flashesThatFeature.entries = + sortedFeatures.map(({flash, track: directlyFeaturedTrack}) => + (directlyFeaturedTrack === track + ? { + flashLink: relation('linkFlash', flash), + } + : { + flashLink: relation('linkFlash', flash), + trackLink: relation('linkTrack', directlyFeaturedTrack), + })); + } + } + + // Section: Lyrics + + if (track.lyrics) { + const lyrics = sections.lyrics = {}; + + lyrics.heading = + relation('generateContentHeading'); + + lyrics.content = + relation('transformContent', track.lyrics); + } + + // Sections: Sheet music files, MIDI/proejct files, additional files + + if (!empty(track.sheetMusicFiles)) { + sections.sheetMusicFiles = additionalFilesSection(track.sheetMusicFiles); + } + + if (!empty(track.midiProjectFiles)) { + sections.midiProjectFiles = additionalFilesSection(track.midiProjectFiles); + } + + if (!empty(track.additionalFiles)) { + sections.additionalFiles = additionalFilesSection(track.additionalFiles); + } + + // Section: Artist commentary + + if (track.commentary) { + const artistCommentary = sections.artistCommentary = {}; + + artistCommentary.heading = + relation('generateContentHeading'); + + artistCommentary.content = + relation('transformContent', track.commentary); + } + + return relations; + }, + + data(sprawl, track) { + const data = {}; + + const {album} = track; + + data.name = track.name; + data.date = track.date; + data.duration = track.duration; + + data.hasUniqueCoverArt = track.hasUniqueCoverArt; + data.hasAlbumCoverArt = album.hasCoverArt; + + if (track.hasUniqueCoverArt) { + data.albumCoverArtDirectory = album.directory; + data.trackCoverArtDirectory = track.directory; + data.coverArtFileExtension = track.coverArtFileExtension; + data.coverNeedsReveal = track.artTags.some(t => t.isContentWarning); + + if (track.coverArtDate && +track.coverArtDate !== +track.date) { + data.coverArtDate = track.coverArtDate; + } + } else if (track.album.hasCoverArt) { + data.albumCoverArtDirectory = album.directory; + data.coverArtFileExtension = album.coverArtFileExtension; + data.coverNeedsReveal = album.artTags.some(t => t.isContentWarning); + } + + if (!empty(track.additionalFiles)) { + data.numAdditionalFiles = track.additionalFiles.length; + } + + return data; + }, + + generate(data, relations, {html, language}) { + const content = {}; + + const {sections: sec} = relations; + + const formatContributions = + (stringKey, contributionLinks, {showContribution = true, showIcons = true} = {}) => + contributionLinks && + language.$(stringKey, { + artists: + language.formatConjunctionList( + contributionLinks.map(link => + link.slots({showContribution, showIcons}))), + }); + + if (data.hasUniqueCoverArt) { + content.cover = relations.cover + .slots({ + path: [ + 'media.trackCover', + data.albumCoverArtDirectory, + data.trackCoverArtDirectory, + data.coverArtFileExtension, + ], + }); + content.coverNeedsReveal = data.coverNeedsReveal; + } else if (data.hasAlbumCoverArt) { + content.cover = relations.cover + .slots({ + path: [ + 'media.albumCover', + data.albumCoverArtDirectory, + data.coverArtFileExtension, + ], + }); + content.coverNeedsReveal = data.coverNeedsReveal; + } else { + content.cover = null; + content.coverNeedsReveal = null; + } + + content.main = { + headingMode: 'sticky', + + content: html.tags([ + html.tag('p', { + [html.onlyIfContent]: true, + [html.joinChildren]: html.tag('br'), + }, [ + formatContributions('releaseInfo.by', sec.releaseInfo.artistContributionLinks), + formatContributions('releaseInfo.coverArtBy', sec.releaseInfo.coverArtistContributionLinks), + + data.date && + language.$('releaseInfo.released', { + date: language.formatDate(data.date), + }), + + data.coverArtDate && + language.$('releaseInfo.artReleased', { + date: language.formatDate(data.coverArtDate), + }), + + data.duration && + language.$('releaseInfo.duration', { + duration: language.formatDuration(data.duration), + }), + ]), + + html.tag('p', + (sec.listen.externalLinks + ? language.$('releaseInfo.listenOn', { + links: language.formatDisjunctionList(sec.listen.externalLinks), + }) + : language.$('releaseInfo.listenOn.noLinks', { + name: html.tag('i', data.name), + }))), + + html.tag('p', + { + [html.onlyIfContent]: true, + [html.joinChildren]: '<br>', + }, + [ + sec.sheetMusicFiles && + language.$('releaseInfo.sheetMusicFiles.shortcut', { + link: html.tag('a', + {href: '#sheet-music-files'}, + language.$('releaseInfo.sheetMusicFiles.shortcut.link')), + }), + + sec.midiProjectFiles && + language.$('releaseInfo.midiProjectFiles.shortcut', { + link: html.tag('a', + {href: '#midi-project-files'}, + language.$('releaseInfo.midiProjectFiles.shortcut.link')), + }), + + sec.additionalFiles && + sec.extra.additionalFilesShortcut, + + sec.artistCommentary && + language.$('releaseInfo.readCommentary', { + link: html.tag('a', + {href: '#artist-commentary'}, + language.$('releaseInfo.readCommentary.link')), + }), + ]), + + sec.otherReleases && [ + sec.otherReleases.heading + .slots({ + id: 'also-released-as', + title: language.$('releaseInfo.alsoReleasedAs'), + }), + + html.tag('ul', + sec.otherReleases.items.map(({trackLink, albumLink}) => + html.tag('li', + language.$('releaseInfo.alsoReleasedAs.item', { + track: trackLink, + album: albumLink, + })))), + ], + + sec.contributors && [ + sec.contributors.heading + .slots({ + id: 'contributors', + title: language.$('releaseInfo.contributors'), + }), + + html.tag('ul', sec.contributors.contributionLinks.map(contributionLink => + html.tag('li', + contributionLink + .slots({ + showIcons: true, + showContribution: true, + })))), + ], + + sec.references && [ + sec.references.heading + .slots({ + id: 'references', + title: + language.$('releaseInfo.tracksReferenced', { + track: html.tag('i', data.name), + }), + }), + + sec.references.list, + ], + + sec.referencedBy && [ + sec.referencedBy.heading + .slots({ + id: 'referenced-by', + title: + language.$('releaseInfo.tracksThatReference', { + track: html.tag('i', data.name), + }), + }), + + sec.referencedBy.list, + ], + + sec.samples && [ + sec.samples.heading + .slots({ + id: 'samples', + title: + language.$('releaseInfo.tracksSampled', { + track: html.tag('i', data.name), + }), + }), + + sec.samples.list, + ], + + sec.sampledBy && [ + sec.sampledBy.heading + .slots({ + id: 'referenced-by', + title: + language.$('releaseInfo.tracksThatSample', { + track: html.tag('i', data.name), + }), + }), + + sec.sampledBy.list, + ], + sec.flashesThatFeature && [ + sec.flashesThatFeature.heading + .slots({ + id: 'featured-in', + title: + language.$('releaseInfo.flashesThatFeature', { + track: html.tag('i', data.name), + }), + }), + + html.tag('ul', sec.flashesThatFeature.entries.map(({flashLink, trackLink}) => + (trackLink + ? html.tag('li', {class: 'rerelease'}, + language.$('releaseInfo.flashesThatFeature.item.asDifferentRelease', { + flash: flashLink, + track: trackLink, + })) + : html.tag('li', + language.$('releaseInfo.flashesThatFeature.item', { + flash: flashLink, + }))))), + ], + + sec.lyrics && [ + sec.lyrics.heading + .slots({ + id: 'lyrics', + title: language.$('releaseInfo.lyrics'), + }), + + html.tag('blockquote', + sec.lyrics.content + .slot('mode', 'lyrics')), + ], + + sec.sheetMusicFiles && [ + sec.sheetMusicFiles.heading + .slots({ + id: 'sheet-music-files', + title: language.$('releaseInfo.sheetMusicFiles.heading'), + }), + + sec.sheetMusicFiles.list, + ], + + sec.midiProjectFiles && [ + sec.midiProjectFiles.heading + .slots({ + id: 'midi-project-files', + title: language.$('releaseInfo.midiProjectFiles.heading'), + }), + + sec.midiProjectFiles.list, + ], + + sec.additionalFiles && [ + sec.additionalFiles.heading + .slots({ + id: 'additional-files', + title: + language.$('releaseInfo.additionalFiles.heading', { + additionalFiles: + language.countAdditionalFiles(data.numAdditionalFiles, {unit: true}), + }), + }), + + sec.additionalFiles.list, + ], + + sec.artistCommentary && [ + sec.artistCommentary.heading + .slots({ + id: 'artist-commentary', + title: language.$('releaseInfo.artistCommentary') + }), + + html.tag('blockquote', + sec.artistCommentary.content + .slot('mode', 'multiline')), + ], + ]), + }; + + return content; + }, +}; + +/* + const generateCommentary = ({language, link, transformMultiline}) => + transformMultiline([ + track.commentary, + ...otherReleases.map((track) => + track.commentary + ?.split('\n') + .filter((line) => line.replace(/<\/b>/g, '').includes(':</i>')) + .flatMap(line => [ + line, + language.$('releaseInfo.artistCommentary.seeOriginalRelease', { + original: link.track(track), + }), + ]) + .join('\n') + ), + ].filter(Boolean).join('\n')); + + const data = { + type: 'data', + path: ['track', track.directory], + data: ({ + serializeContribs, + serializeCover, + serializeGroupsForTrack, + serializeLink, + }) => ({ + name: track.name, + directory: track.directory, + dates: { + released: track.date, + originallyReleased: track.originalDate, + coverArtAdded: track.coverArtDate, + }, + duration: track.duration, + color: track.color, + cover: serializeCover(track, getTrackCover), + artistsContribs: serializeContribs(track.artistContribs), + contributorContribs: serializeContribs(track.contributorContribs), + coverArtistContribs: serializeContribs(track.coverArtistContribs || []), + album: serializeLink(track.album), + groups: serializeGroupsForTrack(track), + references: track.references.map(serializeLink), + referencedBy: track.referencedBy.map(serializeLink), + alsoReleasedAs: otherReleases.map((track) => ({ + track: serializeLink(track), + album: serializeLink(track.album), + })), + }), + }; + + const getSocialEmbedDescription = ({ + getArtistString: _getArtistString, + language, + }) => { + const hasArtists = !empty(track.artistContribs); + const hasCoverArtists = !empty(track.coverArtistContribs); + const getArtistString = (contribs) => + _getArtistString(contribs, { + // We don't want to put actual HTML tags in social embeds (sadly + // they don't get parsed and displayed, generally speaking), so + // override the link argument so that artist "links" just show + // their names. + link: {artist: (artist) => artist.name}, + }); + if (!hasArtists && !hasCoverArtists) return ''; + return language.formatString( + 'trackPage.socialEmbed.body' + + [hasArtists && '.withArtists', hasCoverArtists && '.withCoverArtists'] + .filter(Boolean) + .join(''), + Object.fromEntries( + [ + hasArtists && ['artists', getArtistString(track.artistContribs)], + hasCoverArtists && [ + 'coverArtists', + getArtistString(track.coverArtistContribs), + ], + ].filter(Boolean) + ) + ); + }; + + const page = { + page: () => { + return { + title: language.$('trackPage.title', {track: track.name}), + stylesheet: getAlbumStylesheet(album, {to}), + + themeColor: track.color, + theme: + getThemeString(track.color, { + additionalVariables: [ + `--album-directory: ${album.directory}`, + `--track-directory: ${track.directory}`, + ] + }), + + socialEmbed: { + heading: language.$('trackPage.socialEmbed.heading', { + album: track.album.name, + }), + headingLink: absoluteTo('localized.album', album.directory), + title: language.$('trackPage.socialEmbed.title', { + track: track.name, + }), + description: getSocialEmbedDescription({getArtistString, language}), + image: '/' + getTrackCover(track, {to: urls.from('shared.root').to}), + color: track.color, + }, + + secondaryNav: generateAlbumSecondaryNav(album, track, { + getLinkThemeString, + html, + language, + link, + }), + }; + }, + }; +*/ diff --git a/src/content/dependencies/generateTrackList.js b/src/content/dependencies/generateTrackList.js new file mode 100644 index 00000000..e2e9f48d --- /dev/null +++ b/src/content/dependencies/generateTrackList.js @@ -0,0 +1,55 @@ +import {empty} from '../../util/sugar.js'; + +export default { + contentDependencies: ['linkTrack', 'linkContribution'], + + extraDependencies: ['html', 'language'], + + relations(relation, tracks) { + if (empty(tracks)) { + return {}; + } + + return { + items: tracks.map(track => ({ + trackLink: + relation('linkTrack', track), + + contributionLinks: + track.artistContribs.map(contrib => + relation('linkContribution', contrib.who, contrib.what)), + })), + }; + }, + + generate(relations, {html, language}) { + return html.template({ + annotation: `generateTrackList`, + + slots: { + showContribution: {type: 'boolean', default: false}, + showIcons: {type: 'boolean', default: false}, + }, + + content(slots) { + return html.tag('ul', + relations.items.map(({trackLink, contributionLinks}) => + html.tag('li', + language.$('trackList.item.withArtists', { + track: trackLink, + by: + html.tag('span', {class: 'by'}, + language.$('trackList.item.withArtists.by', { + artists: + language.formatConjunctionList( + contributionLinks.map(link => + link.slots({ + showContribution: slots.showContribution, + showIcons: slots.showIcons, + }))), + })), + })))); + }, + }); + }, +}; diff --git a/src/content/dependencies/generateTrackListDividedByGroups.js b/src/content/dependencies/generateTrackListDividedByGroups.js new file mode 100644 index 00000000..1f1ebef8 --- /dev/null +++ b/src/content/dependencies/generateTrackListDividedByGroups.js @@ -0,0 +1,53 @@ +import {empty} from '../../util/sugar.js'; + +import groupTracksByGroup from '../util/groupTracksByGroup.js'; + +export default { + contentDependencies: ['generateTrackList', 'linkGroup'], + extraDependencies: ['html', 'language'], + + relations(relation, tracks, groups) { + if (empty(tracks)) { + return {}; + } + + if (empty(groups)) { + return { + flatList: + relation('generateTrackList', tracks), + }; + } + + const lists = groupTracksByGroup(tracks, groups); + + return { + groupedLists: + Array.from(lists.entries()).map(([groupOrOther, tracks]) => ({ + ...(groupOrOther === 'other' + ? {other: true} + : {groupLink: relation('linkGroup', groupOrOther)}), + + list: + relation('generateTrackList', tracks), + })), + }; + }, + + generate(relations, {html, language}) { + if (relations.flatList) { + return relations.flatList; + } + + return html.tag('dl', + relations.groupedLists.map(({other, groupLink, list}) => [ + html.tag('dt', + (other + ? language.$('trackList.group.fromOther') + : language.$('trackList.group', { + group: groupLink + }))), + + html.tag('dd', list), + ])); + }, +}; diff --git a/src/content/dependencies/image.js b/src/content/dependencies/image.js new file mode 100644 index 00000000..f9cb00bf --- /dev/null +++ b/src/content/dependencies/image.js @@ -0,0 +1,207 @@ +import {empty} from '../../util/sugar.js'; + +export default { + extraDependencies: [ + 'getSizeOfImageFile', + 'html', + 'language', + 'thumb', + 'to', + ], + + data(artTags) { + const data = {}; + + if (artTags) { + data.contentWarnings = + artTags + .filter(tag => tag.isContentWarning) + .map(tag => tag.name); + } else { + data.contentWarnings = null; + } + + return data; + }, + + generate(data, { + getSizeOfImageFile, + html, + language, + thumb, + to, + }) { + return html.template({ + annotation: 'image', + + slots: { + src: { + type: 'string', + }, + + path: { + validate: v => v.validateArrayItems(v.isString), + }, + + thumb: {type: 'string'}, + + reveal: {type: 'boolean', default: true}, + link: {type: 'boolean', default: false}, + lazy: {type: 'boolean', default: false}, + square: {type: 'boolean', default: false}, + + id: {type: 'string'}, + alt: {type: 'string'}, + width: {type: 'number'}, + height: {type: 'number'}, + + missingSourceContent: {type: 'html'}, + }, + + content(slots) { + let originalSrc; + + if (slots.src) { + originalSrc = slots.src; + } else if (!empty(slots.path)) { + originalSrc = to(...slots.path); + } else { + originalSrc = ''; + } + + const thumbSrc = + originalSrc && + (slots.thumb + ? thumb[slots.thumb](originalSrc) + : originalSrc); + + const willLink = typeof slots.link === 'string' || slots.link; + + const willReveal = + slots.reveal && + originalSrc && + !empty(data.contentWarnings); + + const willSquare = slots.square; + + const idOnImg = willLink ? null : slots.id; + const idOnLink = willLink ? slots.id : null; + + if (!originalSrc) { + return prepare( + html.tag('div', {class: 'image-text-area'}, + slots.missingSourceContent)); + } + + let fileSize = null; + if (willLink) { + const mediaRoot = to('media.root'); + if (originalSrc.startsWith(mediaRoot)) { + fileSize = + getSizeOfImageFile( + originalSrc + .slice(mediaRoot.length) + .replace(/^\//, '')); + } + } + + let reveal = null; + if (willReveal) { + reveal = [ + language.$('misc.contentWarnings', { + warnings: language.formatUnitList(data.contentWarnings), + }), + html.tag('br'), + html.tag('span', {class: 'reveal-interaction'}, + language.$('misc.contentWarnings.reveal')), + ]; + } + + const imgAttributes = { + id: idOnImg, + alt: slots.alt, + width: slots.width, + height: slots.height, + 'data-original-size': fileSize, + }; + + const nonlazyHTML = + originalSrc && + prepare( + html.tag('img', { + ...imgAttributes, + src: thumbSrc, + })); + + if (slots.lazy) { + return html.tags([ + html.tag('noscript', nonlazyHTML), + prepare( + html.tag('img', + { + ...imgAttributes, + class: 'lazy', + 'data-original': thumbSrc, + }), + true), + ]); + } + + return nonlazyHTML; + + function prepare(content, hide = false) { + let wrapped = content; + + wrapped = + html.tag('div', {class: 'image-container'}, + html.tag('div', {class: 'image-inner-area'}, + wrapped)); + + if (willReveal) { + wrapped = + html.tag('div', {class: 'reveal'}, [ + wrapped, + html.tag('span', {class: 'reveal-text-container'}, + html.tag('span', {class: 'reveal-text'}, + reveal)), + ]); + } + + if (willSquare) { + wrapped = + html.tag('div', + { + class: [ + 'square', + hide && !willLink && 'js-hide' + ], + }, + + html.tag('div', {class: 'square-content'}, + wrapped)); + } + + if (willLink) { + wrapped = html.tag('a', + { + id: idOnLink, + class: [ + 'box', + 'image-link', + hide && 'js-hide', + ], + + href: + (typeof slots.link === 'string' + ? slots.link + : originalSrc), + }, + wrapped); + } + + return wrapped; + } + }, + }); + } +}; diff --git a/src/content/dependencies/index.js b/src/content/dependencies/index.js new file mode 100644 index 00000000..1210d78e --- /dev/null +++ b/src/content/dependencies/index.js @@ -0,0 +1,235 @@ +import chokidar from 'chokidar'; +import EventEmitter from 'events'; +import * as path from 'path'; +import {ESLint} from 'eslint'; +import {fileURLToPath} from 'url'; + +import contentFunction from '../../content-function.js'; +import {color, logWarn} from '../../util/cli.js'; +import {annotateFunction} from '../../util/sugar.js'; + +function cachebust(filePath) { + if (filePath in cachebust.cache) { + cachebust.cache[filePath] += 1; + return `${filePath}?cachebust${cachebust.cache[filePath]}`; + } else { + cachebust.cache[filePath] = 0; + return filePath; + } +} + +cachebust.cache = Object.create(null); + +export function watchContentDependencies({ + mock = null, + logging = true, +} = {}) { + const events = new EventEmitter(); + const contentDependencies = {}; + + let emittedReady = false; + let initialScanComplete = false; + let allDependenciesFulfilled = false; + + Object.assign(events, { + contentDependencies, + close, + }); + + const eslint = new ESLint(); + + // Watch adjacent files + const metaPath = fileURLToPath(import.meta.url); + const metaDirname = path.dirname(metaPath); + const watcher = chokidar.watch(metaDirname); + + watcher.on('all', (event, filePath) => { + if (!['add', 'change'].includes(event)) return; + if (filePath === metaPath) return; + handlePathUpdated(filePath); + }); + + watcher.on('unlink', (filePath) => { + if (filePath === metaPath) { + console.error(`Yeowzers content dependencies just got nuked.`); + return; + } + handlePathRemoved(filePath); + }); + + watcher.on('ready', () => { + initialScanComplete = true; + checkReadyConditions(); + }); + + if (mock) { + const errors = []; + for (const [functionName, spec] of Object.entries(mock)) { + try { + const fn = processFunctionSpec(functionName, spec); + contentDependencies[functionName] = fn; + } catch (error) { + error.message = `(${functionName}) ${error.message}`; + errors.push(error); + } + } + if (errors.length) { + throw new AggregateError(errors, `Errors processing mocked content functions`); + } + checkReadyConditions(); + } + + return events; + + async function close() { + return watcher.close(); + } + + function checkReadyConditions() { + if (emittedReady) { + return; + } + + if (!initialScanComplete) { + return; + } + + checkAllDependenciesFulfilled(); + + if (!allDependenciesFulfilled) { + return; + } + + events.emit('ready'); + emittedReady = true; + } + + function checkAllDependenciesFulfilled() { + allDependenciesFulfilled = !Object.values(contentDependencies).includes(null); + } + + function getFunctionName(filePath) { + const shortPath = path.basename(filePath); + const functionName = shortPath.slice(0, -path.extname(shortPath).length); + return functionName; + } + + function isMocked(functionName) { + return !!mock && Object.keys(mock).includes(functionName); + } + + async function handlePathRemoved(filePath) { + const functionName = getFunctionName(filePath); + if (isMocked(functionName)) return; + + delete contentDependencies[functionName]; + } + + async function handlePathUpdated(filePath) { + const functionName = getFunctionName(filePath); + if (isMocked(functionName)) return; + + let error = null; + + main: { + const eslintResults = await eslint.lintFiles([filePath]); + const eslintFormatter = await eslint.loadFormatter('stylish'); + const eslintResultText = eslintFormatter.format(eslintResults); + if (eslintResultText.trim().length) { + console.log(eslintResultText); + } + + let spec; + try { + spec = (await import(cachebust(filePath))).default; + } catch (caughtError) { + error = caughtError; + error.message = `Error importing: ${error.message}`; + break main; + } + + let fn; + try { + fn = processFunctionSpec(functionName, spec); + } catch (caughtError) { + error = caughtError; + break main; + } + + if (logging && initialScanComplete) { + const timestamp = new Date().toLocaleString('en-US', {timeStyle: 'medium'}); + console.log(color.green(`[${timestamp}] Updated ${functionName}`)); + } + + contentDependencies[functionName] = fn; + + events.emit('update', functionName); + checkReadyConditions(); + } + + if (!error) { + return true; + } + + if (!(functionName in contentDependencies)) { + contentDependencies[functionName] = null; + } + + events.emit('error', functionName, error); + + if (logging) { + if (contentDependencies[functionName]) { + logWarn`Failed to import ${functionName} - using existing version`; + } else { + logWarn`Failed to import ${functionName} - no prior version loaded`; + } + + if (typeof error === 'string') { + console.error(color.yellow(error)); + } else { + console.error(error); + } + } + + return false; + } + + function processFunctionSpec(functionName, spec) { + if (typeof spec.data === 'function') { + annotateFunction(spec.data, {name: functionName, description: 'data'}); + } + + if (typeof spec.generate === 'function') { + annotateFunction(spec.generate, {name: functionName}); + } + + let fn; + try { + fn = contentFunction(spec); + } catch (error) { + error.message = `Error loading spec: ${error.message}`; + throw error; + } + + return fn; + } +} + +export function quickLoadContentDependencies(opts) { + return new Promise((resolve, reject) => { + const watcher = watchContentDependencies(opts); + + watcher.on('error', (name, error) => { + watcher.close().then(() => { + error.message = `Error loading dependency ${name}: ${error}`; + reject(error); + }); + }); + + watcher.on('ready', () => { + watcher.close().then(() => { + resolve(watcher.contentDependencies); + }); + }); + }); +} diff --git a/src/content/dependencies/linkAlbum.js b/src/content/dependencies/linkAlbum.js new file mode 100644 index 00000000..36b0d13a --- /dev/null +++ b/src/content/dependencies/linkAlbum.js @@ -0,0 +1,8 @@ +export default { + contentDependencies: ['linkThing'], + + relations: (relation, album) => + ({link: relation('linkThing', 'localized.album', album)}), + + generate: (relations) => relations.link, +}; diff --git a/src/content/dependencies/linkAlbumAdditionalFile.js b/src/content/dependencies/linkAlbumAdditionalFile.js new file mode 100644 index 00000000..27c0ba9c --- /dev/null +++ b/src/content/dependencies/linkAlbumAdditionalFile.js @@ -0,0 +1,26 @@ +export default { + contentDependencies: [ + 'linkTemplate', + ], + + relations(relation) { + return { + linkTemplate: relation('linkTemplate'), + }; + }, + + data(album, file) { + return { + albumDirectory: album.directory, + file, + }; + }, + + generate(data, relations) { + return relations.linkTemplate + .slots({ + path: ['media.albumAdditionalFile', data.albumDirectory, data.file], + content: data.file, + }); + }, +}; diff --git a/src/content/dependencies/linkAlbumCommentary.js b/src/content/dependencies/linkAlbumCommentary.js new file mode 100644 index 00000000..ab519fd6 --- /dev/null +++ b/src/content/dependencies/linkAlbumCommentary.js @@ -0,0 +1,8 @@ +export default { + contentDependencies: ['linkThing'], + + relations: (relation, album) => + ({link: relation('linkThing', 'localized.albumCommentary', album)}), + + generate: (relations) => relations.link, +}; diff --git a/src/content/dependencies/linkAlbumGallery.js b/src/content/dependencies/linkAlbumGallery.js new file mode 100644 index 00000000..e3f30a29 --- /dev/null +++ b/src/content/dependencies/linkAlbumGallery.js @@ -0,0 +1,8 @@ +export default { + contentDependencies: ['linkThing'], + + relations: (relation, album) => + ({link: relation('linkThing', 'localized.albumGallery', album)}), + + generate: (relations) => relations.link, +}; diff --git a/src/content/dependencies/linkArtTag.js b/src/content/dependencies/linkArtTag.js new file mode 100644 index 00000000..7ddb7786 --- /dev/null +++ b/src/content/dependencies/linkArtTag.js @@ -0,0 +1,8 @@ +export default { + contentDependencies: ['linkThing'], + + relations: (relation, artTag) => + ({link: relation('linkThing', 'localized.tag', artTag)}), + + generate: (relations) => relations.link, +}; diff --git a/src/content/dependencies/linkArtist.js b/src/content/dependencies/linkArtist.js new file mode 100644 index 00000000..718ee6fa --- /dev/null +++ b/src/content/dependencies/linkArtist.js @@ -0,0 +1,8 @@ +export default { + contentDependencies: ['linkThing'], + + relations: (relation, artist) => + ({link: relation('linkThing', 'localized.artist', artist)}), + + generate: (relations) => relations.link, +}; diff --git a/src/content/dependencies/linkArtistGallery.js b/src/content/dependencies/linkArtistGallery.js new file mode 100644 index 00000000..66dc172d --- /dev/null +++ b/src/content/dependencies/linkArtistGallery.js @@ -0,0 +1,8 @@ +export default { + contentDependencies: ['linkThing'], + + relations: (relation, artist) => + ({link: relation('linkThing', 'localized.artistGallery', artist)}), + + generate: (relations) => relations.link, +}; diff --git a/src/content/dependencies/linkContribution.js b/src/content/dependencies/linkContribution.js new file mode 100644 index 00000000..1d0e2d6a --- /dev/null +++ b/src/content/dependencies/linkContribution.js @@ -0,0 +1,74 @@ +import {empty} from '../../util/sugar.js'; + +export default { + contentDependencies: [ + 'linkArtist', + 'linkExternalAsIcon', + ], + + extraDependencies: [ + 'html', + 'language', + ], + + relations(relation, artist) { + const relations = {}; + + relations.artistLink = relation('linkArtist', artist); + + relations.artistIcons = + (artist.urls ?? []).map(url => + relation('linkExternalAsIcon', url)); + + return relations; + }, + + data(artist, contribution) { + return {contribution}; + }, + + generate(data, relations, { + html, + language, + }) { + return html.template({ + annotation: 'linkContribution', + + slots: { + showContribution: {type: 'boolean', default: false}, + showIcons: {type: 'boolean', default: false}, + }, + + content(slots) { + const hasContributionPart = !!(slots.showContribution && data.contribution); + const hasExternalPart = !!(slots.showIcons && !empty(relations.artistIcons)); + + const externalLinks = hasExternalPart && + html.tag('span', + {[html.noEdgeWhitespace]: true, class: 'icons'}, + language.formatUnitList(relations.artistIcons)); + + return ( + (hasContributionPart + ? (hasExternalPart + ? language.$('misc.artistLink.withContribution.withExternalLinks', { + artist: relations.artistLink, + contrib: data.contribution, + links: externalLinks, + }) + : language.$('misc.artistLink.withContribution', { + artist: relations.artistLink, + contrib: data.contribution, + })) + : (hasExternalPart + ? language.$('misc.artistLink.withExternalLinks', { + artist: relations.artistLink, + links: externalLinks, + }) + : language.$('misc.artistLink', { + artist: relations.artistLink, + })))); + }, + }); + }, +}; diff --git a/src/content/dependencies/linkExternal.js b/src/content/dependencies/linkExternal.js new file mode 100644 index 00000000..08191a21 --- /dev/null +++ b/src/content/dependencies/linkExternal.js @@ -0,0 +1,93 @@ +// TODO: Define these as extra dependencies and pass them somewhere +const BANDCAMP_DOMAINS = ['bc.s3m.us', 'music.solatrux.com']; +const MASTODON_DOMAINS = ['types.pl']; + +export default { + extraDependencies: ['html', 'language'], + + data(url, { + type = 'generic', + } = {}) { + const types = ['generic', 'album']; + if (!types.includes(type)) { + throw new TypeError(`Expected type to be one of ${types}`); + } + + return { + url, + type, + }; + }, + + generate(data, {html, language}) { + let isLocal; + let domain; + try { + domain = new URL(data.url).hostname; + } catch (error) { + // No support for relative local URLs yet, sorry! (I.e, local URLs must + // be absolute relative to the domain name in order to work.) + isLocal = true; + } + + const a = html.tag('a', + { + href: data.url, + class: 'nowrap', + }, + + // truly unhinged indentation here + isLocal + ? language.$('misc.external.local') + + : domain.includes('bandcamp.com') + ? language.$('misc.external.bandcamp') + + : BANDCAMP_DOMAINS.includes(domain) + ? language.$('misc.external.bandcamp.domain', {domain}) + + : MASTODON_DOMAINS.includes(domain) + ? language.$('misc.external.mastodon.domain', {domain}) + + : domain.includes('youtu') + ? data.type === 'album' + ? data.url.includes('list=') + ? language.$('misc.external.youtube.playlist') + : language.$('misc.external.youtube.fullAlbum') + : language.$('misc.external.youtube') + + : domain.includes('soundcloud') + ? language.$('misc.external.soundcloud') + + : domain.includes('tumblr.com') + ? language.$('misc.external.tumblr') + + : domain.includes('twitter.com') + ? language.$('misc.external.twitter') + + : domain.includes('deviantart.com') + ? language.$('misc.external.deviantart') + + : domain.includes('wikipedia.org') + ? language.$('misc.external.wikipedia') + + : domain.includes('poetryfoundation.org') + ? language.$('misc.external.poetryFoundation') + + : domain.includes('instagram.com') + ? language.$('misc.external.instagram') + + : domain.includes('patreon.com') + ? language.$('misc.external.patreon') + + : domain.includes('spotify.com') + ? language.$('misc.external.spotify') + + : domain.includes('newgrounds.com') + ? language.$('misc.external.newgrounds') + + : domain); + + return a; + } +}; diff --git a/src/content/dependencies/linkExternalAsIcon.js b/src/content/dependencies/linkExternalAsIcon.js new file mode 100644 index 00000000..6496d026 --- /dev/null +++ b/src/content/dependencies/linkExternalAsIcon.js @@ -0,0 +1,46 @@ +// TODO: Define these as extra dependencies and pass them somewhere +const BANDCAMP_DOMAINS = ['bc.s3m.us', 'music.solatrux.com']; +const MASTODON_DOMAINS = ['types.pl']; + +export default { + extraDependencies: ['html', 'language', 'to'], + + data(url) { + return {url}; + }, + + generate(data, {html, language, to}) { + const domain = new URL(data.url).hostname; + const [id, msg] = ( + domain.includes('bandcamp.com') + ? ['bandcamp', language.$('misc.external.bandcamp')] + : BANDCAMP_DOMAINS.includes(domain) + ? ['bandcamp', language.$('misc.external.bandcamp.domain', {domain})] + : MASTODON_DOMAINS.includes(domain) + ? ['mastodon', language.$('misc.external.mastodon.domain', {domain})] + : domain.includes('youtu') + ? ['youtube', language.$('misc.external.youtube')] + : domain.includes('soundcloud') + ? ['soundcloud', language.$('misc.external.soundcloud')] + : domain.includes('tumblr.com') + ? ['tumblr', language.$('misc.external.tumblr')] + : domain.includes('twitter.com') + ? ['twitter', language.$('misc.external.twitter')] + : domain.includes('deviantart.com') + ? ['deviantart', language.$('misc.external.deviantart')] + : domain.includes('instagram.com') + ? ['instagram', language.$('misc.external.bandcamp')] + : domain.includes('newgrounds.com') + ? ['newgrounds', language.$('misc.external.newgrounds')] + : ['globe', language.$('misc.external.domain', {domain})]); + + return html.tag('a', + {href: data.url, class: 'icon'}, + html.tag('svg', [ + html.tag('title', msg), + html.tag('use', { + href: to('shared.staticFile', `icons.svg#icon-${id}`), + }), + ])); + }, +}; diff --git a/src/content/dependencies/linkExternalFlash.js b/src/content/dependencies/linkExternalFlash.js new file mode 100644 index 00000000..65158ff8 --- /dev/null +++ b/src/content/dependencies/linkExternalFlash.js @@ -0,0 +1,41 @@ +// Note: This function is seriously hard-coded for HSMusic, with custom +// presentation of links to Homestuck flashes hosted various places. + +export default { + contentDependencies: ['linkExternal'], + extraDependencies: ['html', 'language'], + + relations(relation, url) { + return { + link: relation('linkExternal', url), + }; + }, + + data(url, flash) { + return { + url, + page: flash.page, + }; + }, + + generate(data, relations, {html, language}) { + const {link} = relations; + const {url, page} = data; + + return html.tag('span', + {class: 'nowrap'}, + + url.includes('homestuck.com') + ? isNaN(Number(page)) + ? language.$('misc.external.flash.homestuck.secret', {link}) + : language.$('misc.external.flash.homestuck.page', {link, page}) + + : url.includes('bgreco.net') + ? language.$('misc.external.flash.bgreco', {link}) + + : url.includes('youtu') + ? language.$('misc.external.flash.youtube', {link}) + + : link); + }, +}; diff --git a/src/content/dependencies/linkFlash.js b/src/content/dependencies/linkFlash.js new file mode 100644 index 00000000..93dd5a28 --- /dev/null +++ b/src/content/dependencies/linkFlash.js @@ -0,0 +1,8 @@ +export default { + contentDependencies: ['linkThing'], + + relations: (relation, flash) => + ({link: relation('linkThing', 'localized.flash', flash)}), + + generate: (relations) => relations.link, +}; diff --git a/src/content/dependencies/linkGroup.js b/src/content/dependencies/linkGroup.js new file mode 100644 index 00000000..ebab1b5b --- /dev/null +++ b/src/content/dependencies/linkGroup.js @@ -0,0 +1,8 @@ +export default { + contentDependencies: ['linkThing'], + + relations: (relation, group) => + ({link: relation('linkThing', 'localized.groupInfo', group)}), + + generate: (relations) => relations.link, +}; diff --git a/src/content/dependencies/linkGroupGallery.js b/src/content/dependencies/linkGroupGallery.js new file mode 100644 index 00000000..86c4a0f3 --- /dev/null +++ b/src/content/dependencies/linkGroupGallery.js @@ -0,0 +1,8 @@ +export default { + contentDependencies: ['linkThing'], + + relations: (relation, group) => + ({link: relation('linkThing', 'localized.groupGallery', group)}), + + generate: (relations) => relations.link, +}; diff --git a/src/content/dependencies/linkListing.js b/src/content/dependencies/linkListing.js new file mode 100644 index 00000000..f27d93ac --- /dev/null +++ b/src/content/dependencies/linkListing.js @@ -0,0 +1,8 @@ +export default { + contentDependencies: ['linkThing'], + + relations: (relation, listing) => + ({link: relation('linkThing', 'localized.listing', listing)}), + + generate: (relations) => relations.link, +}; diff --git a/src/content/dependencies/linkNewsEntry.js b/src/content/dependencies/linkNewsEntry.js new file mode 100644 index 00000000..1fb32dd9 --- /dev/null +++ b/src/content/dependencies/linkNewsEntry.js @@ -0,0 +1,8 @@ +export default { + contentDependencies: ['linkThing'], + + relations: (relation, newsEntry) => + ({link: relation('linkThing', 'localized.newsEntry', newsEntry)}), + + generate: (relations) => relations.link, +}; diff --git a/src/content/dependencies/linkStaticPage.js b/src/content/dependencies/linkStaticPage.js new file mode 100644 index 00000000..032af6c9 --- /dev/null +++ b/src/content/dependencies/linkStaticPage.js @@ -0,0 +1,8 @@ +export default { + contentDependencies: ['linkThing'], + + relations: (relation, staticPage) => + ({link: relation('linkThing', 'localized.staticPage', staticPage)}), + + generate: (relations) => relations.link, +}; diff --git a/src/content/dependencies/linkTemplate.js b/src/content/dependencies/linkTemplate.js new file mode 100644 index 00000000..9109ab50 --- /dev/null +++ b/src/content/dependencies/linkTemplate.js @@ -0,0 +1,73 @@ +import {empty} from '../../util/sugar.js'; + +export default { + extraDependencies: [ + 'appendIndexHTML', + 'getColors', + 'html', + 'to', + ], + + generate({ + appendIndexHTML, + getColors, + html, + to, + }) { + return html.template({ + annotation: 'linkTemplate', + + slots: { + href: {type: 'string'}, + path: {validate: v => v.validateArrayItems(v.isString)}, + hash: {type: 'string'}, + + tooltip: {validate: v => v.isString}, + attributes: {validate: v => v.isAttributes}, + color: {validate: v => v.isColor}, + content: {type: 'html'}, + }, + + content(slots) { + let href = slots.href; + let style; + let title; + + if (!href && !empty(slots.path)) { + href = to(...slots.path); + } + + if (appendIndexHTML) { + if ( + /^(?!https?:\/\/).+\/$/.test(href) && + href.endsWith('/') + ) { + href += 'index.html'; + } + } + + if (slots.hash) { + href += (slots.hash.startsWith('#') ? '' : '#') + slots.hash; + } + + if (slots.color) { + const {primary, dim} = getColors(slots.color); + style = `--primary-color: ${primary}; --dim-color: ${dim}`; + } + + if (slots.tooltip) { + title = slots.tooltip; + } + + return html.tag('a', + { + ...slots.attributes ?? {}, + href, + style, + title, + }, + slots.content); + }, + }); + }, +} diff --git a/src/content/dependencies/linkThing.js b/src/content/dependencies/linkThing.js new file mode 100644 index 00000000..1e648ee6 --- /dev/null +++ b/src/content/dependencies/linkThing.js @@ -0,0 +1,91 @@ +export default { + contentDependencies: [ + 'linkTemplate', + ], + + extraDependencies: [ + 'html', + ], + + relations(relation) { + return { + linkTemplate: relation('linkTemplate'), + }; + }, + + data(pathKey, thing) { + return { + pathKey, + + color: thing.color, + directory: thing.directory, + + name: thing.name, + nameShort: thing.nameShort, + }; + }, + + generate(data, relations, {html}) { + const path = [data.pathKey, data.directory]; + + return html.template({ + annotation: 'linkThing', + + slots: { + content: relations.linkTemplate.getSlotDescription('content'), + preferShortName: {type: 'boolean', default: false}, + + tooltip: { + validate: v => v.oneOf(v.isBoolean, v.isString), + default: false, + }, + + color: { + validate: v => v.oneOf(v.isBoolean, v.isColor), + default: true, + }, + + attributes: relations.linkTemplate.getSlotDescription('attributes'), + hash: relations.linkTemplate.getSlotDescription('hash'), + }, + + content(slots) { + let content = slots.content; + + const name = + (slots.preferShortName + ? data.nameShort ?? data.name + : data.name); + + if (html.isBlank(content)) { + content = name; + } + + let color = null; + if (slots.color === true) { + color = data.color ?? null; + } else if (typeof slots.color === 'string') { + color = slots.color; + } + + let tooltip = null; + if (slots.tooltip === true) { + tooltip = name; + } else if (typeof slots.tooltip === 'string') { + tooltip = slots.tooltip; + } + + return relations.linkTemplate + .slots({ + path, + content, + color, + tooltip, + + attributes: slots.attributes, + hash: slots.hash, + }); + }, + }); + }, +} diff --git a/src/content/dependencies/linkTrack.js b/src/content/dependencies/linkTrack.js new file mode 100644 index 00000000..d5d96726 --- /dev/null +++ b/src/content/dependencies/linkTrack.js @@ -0,0 +1,8 @@ +export default { + contentDependencies: ['linkThing'], + + relations: (relation, track) => + ({link: relation('linkThing', 'localized.track', track)}), + + generate: (relations) => relations.link, +}; diff --git a/src/content/dependencies/transformContent.js b/src/content/dependencies/transformContent.js new file mode 100644 index 00000000..bf4233fd --- /dev/null +++ b/src/content/dependencies/transformContent.js @@ -0,0 +1,325 @@ +import {marked} from 'marked'; + +import {bindFind} from '../../util/find.js'; +import {parseInput} from '../../util/replacer.js'; +import {replacerSpec} from '../../util/transform-content.js'; + +const linkThingRelationMap = { + album: 'linkAlbum', + albumCommentary: 'linkAlbumCommentary', + albumGallery: 'linkAlbumGallery', + artist: 'linkArtist', + artistGallery: 'linkArtistGallery', + flash: 'linkFlash', + groupInfo: 'linkGroup', + groupGallery: 'linkGroupGallery', + listing: 'linkListing', + newsEntry: 'linkNewsEntry', + staticPage: 'linkStaticPage', + tag: 'linkArtTag', + track: 'linkTrack', +}; + +const linkValueRelationMap = { + // media: 'linkPathFromMedia', + // root: 'linkPathFromRoot', + // site: 'linkPathFromSite', +}; + +const linkIndexRelationMap = { + // commentaryIndex: 'linkCommentaryIndex', + // flashIndex: 'linkFlashIndex', + // home: 'linkHome', + // listingIndex: 'linkListingIndex', + // newsIndex: 'linkNewsIndex', +}; + +function getPlaceholder(node, content) { + return {type: 'text', data: content.slice(node.i, node.iEnd)}; +} + +export default { + contentDependencies: [ + ...Object.values(linkThingRelationMap), + ...Object.values(linkValueRelationMap), + ...Object.values(linkIndexRelationMap), + ], + + extraDependencies: ['html', 'language', 'wikiData'], + + sprawl(wikiData, content) { + const find = bindFind(wikiData); + + const parsedNodes = parseInput(content); + + return { + nodes: parsedNodes + .map(node => { + if (node.type !== 'tag') { + return node; + } + + const placeholder = getPlaceholder(node, content); + + const replacerKeyImplied = !node.data.replacerKey; + const replacerKey = replacerKeyImplied ? 'track' : node.data.replacerKey.data; + + // TODO: We don't support recursive nodes like before, at the moment. Sorry! + // const replacerValue = transformNodes(node.data.replacerValue, opts); + const replacerValue = node.data.replacerValue[0].data; + + const spec = replacerSpec[replacerKey]; + + if (!spec) { + return placeholder; + } + + if (spec.link) { + let data = {key: spec.link}; + + determineData: { + // No value at all: this is an index link. + if (!replacerValue) { + break determineData; + } + + // Nothing to find: the link operates on a path or string, not a data object. + if (!spec.find) { + data.value = replacerValue; + break determineData; + } + + const thing = + find[spec.find]( + (replacerKeyImplied + ? replacerValue + : replacerKey + `:` + replacerValue), + wikiData); + + // Nothing was found: this is unexpected, so return placeholder. + if (!thing) { + return placeholder; + } + + // Something was found: the link operates on that thing. + data.thing = thing; + } + + const {transformName} = spec; + + // TODO: Again, no recursive nodes. Sorry! + // const enteredLabel = node.data.label && transformNode(node.data.label, opts); + const enteredLabel = node.data.label?.data; + const enteredHash = node.data.hash?.data; + + data.label = + enteredLabel ?? + (transformName && data.thing.name + ? transformName(data.thing.name, node, content) + : null); + + data.hash = enteredHash ?? null; + + return {i: node.i, iEnd: node.iEnd, type: 'link', data}; + } + + // This will be another {type: 'tag'} node which gets processed in + // generate. + return node; + }), + }; + }, + + data(sprawl, content) { + return { + content, + + nodes: + sprawl.nodes + .map(node => { + // Replace link nodes with a stub. It'll be replaced (by position) + // with an item from relations. + if (node.type === 'link') { + return {type: 'link'}; + } + + // Other nodes will get processed in generate. + return node; + }), + }; + }, + + relations(relation, sprawl, content) { + const {nodes} = sprawl; + + const relationOrPlaceholder = + (node, name, arg) => + (name + ? { + link: relation(name, arg), + label: node.data.label, + hash: node.data.hash, + } + : getPlaceholder(node, content)); + + return { + links: + nodes + .filter(({type}) => type === 'link') + .map(node => { + const {key, thing, value} = node.data; + + if (thing) { + return relationOrPlaceholder(node, linkThingRelationMap[key], thing); + } else if (value) { + return relationOrPlaceholder(node, linkValueRelationMap[key], value); + } else { + return relationOrPlaceholder(node, linkIndexRelationMap[key]); + } + }), + }; + }, + + generate(data, relations, {html, language}) { + let linkIndex = 0; + + // This array contains only straight text and link nodes, which are directly + // representable in html (so no further processing is needed on the level of + // individual nodes). + const contentFromNodes = + data.nodes.map(node => { + if (node.type === 'text') { + return {type: 'text', data: node.data}; + } + + if (node.type === 'link') { + const linkNode = relations.links[linkIndex++]; + if (linkNode.type === 'text') { + return {type: 'text', data: linkNode.data}; + } + + const {link, label, hash} = linkNode; + + return { + type: 'link', + data: link.slots({content: label, hash}), + }; + } + + if (node.type === 'tag') { + const {replacerKey, replacerValue} = node.data; + + const spec = replacerSpec[replacerKey]; + + if (!spec) { + return getPlaceholder(node, data.content); + } + + const {value: valueFn, html: htmlFn} = spec; + + const value = + (valueFn + ? valueFn(replacerValue) + : replacerValue); + + const contents = + (htmlFn + ? htmlFn(value, {html, language}) + : value); + + return {type: 'text', data: contents}; + } + + return getPlaceholder(node, data.content); + }); + + return html.template({ + annotation: `transformContent`, + + slots: { + mode: { + validate: v => v.is('inline', 'multiline', 'lyrics'), + default: 'multiline', + }, + }, + + content(slots) { + // In inline mode, no further processing is needed! + + if (slots.mode === 'inline') { + return html.tags(contentFromNodes.map(node => node.data)); + } + + // Multiline mode has a secondary processing stage where it's passed... + // through marked! Rolling your own Markdown only gets you so far :D + + const markedOptions = { + headerIds: false, + mangle: false, + }; + + // This is separated into its own function just since we're gonna reuse + // it in a minute if everything goes to heck in lyrics mode. + const transformMultiline = () => { + const markedInput = + contentFromNodes + .map(node => { + if (node.type === 'text') { + return node.data; + } else { + return node.data.toString(); + } + }) + .join('') + + // Compress multiple line breaks into single line breaks. + .replace(/\n{2,}/g, '\n') + // Expand line breaks which don't follow a list. + .replace(/(?<!^ *-.*)\n+/gm, '\n\n') + // Expand line breaks which are at the end of a list. + .replace(/(?<=^ *-.*)\n+(?!^ *-)/gm, '\n\n'); + + return marked.parse(markedInput, markedOptions); + } + + if (slots.mode === 'multiline') { + // Unfortunately, we kind of have to be super evil here and stringify + // the links, or else parse marked's output into html tags, which is + // very out of scope at the moment. + return transformMultiline(); + } + + // Lyrics mode goes through marked too, but line breaks are processed + // differently. Instead of having each line get its own paragraph, + // "adjacent" lines are joined together (with blank lines separating + // each verse/paragraph). + + if (slots.mode === 'lyrics') { + // If it looks like old data, using <br> instead of bunched together + // lines... then oh god... just use transformMultiline. Perishes. + if ( + contentFromNodes.some(node => + node.type === 'text' && + node.data.includes('<br')) + ) { + return transformMultiline(); + } + + // Lyrics mode is also evil for the same stringifying reasons as + // multiline. + return marked.parse( + contentFromNodes + .map(node => { + if (node.type === 'text') { + return node.data.replace(/\b\n\b/g, '<br>\n'); + } else { + return node.data.toString(); + } + }) + .join(''), + markedOptions); + } + }, + }); + }, +} diff --git a/src/content/util/getChronologyRelations.js b/src/content/util/getChronologyRelations.js new file mode 100644 index 00000000..11281e75 --- /dev/null +++ b/src/content/util/getChronologyRelations.js @@ -0,0 +1,42 @@ +export default function getChronologyRelations(thing, { + contributions, + linkArtist, + linkThing, + getThings, +}) { + // One call to getChronologyRelations is considered "lumping" together all + // contributions as carrying equivalent meaning (for example, "artist" + // contributions and "contributor" contributions are bunched together in + // one call to getChronologyRelations, while "cover artist" contributions + // are a separate call). getChronologyRelations prevents duplicates that + // carry the same meaning by only using the first instance of each artist + // in the contributions array passed to it. It's expected that the string + // identifying which kind of contribution ("track" or "cover art") is + // shared and applied to all contributions, as providing them together + // in one call to getChronologyRelations implies they carry the same + // meaning. + + const artistsSoFar = new Set(); + + contributions = contributions.filter(({who}) => { + if (artistsSoFar.has(who)) { + return false; + } else { + artistsSoFar.add(who); + return true; + } + }); + + return contributions.map(({who}) => { + const things = Array.from(new Set(getThings(who))); + const index = things.indexOf(thing); + const previous = things[index - 1]; + const next = things[index + 1]; + return { + index: index + 1, + artistLink: linkArtist(who), + previousLink: previous ? linkThing(previous) : null, + nextLink: next ? linkThing(next) : null, + }; + }); +} diff --git a/src/content/util/groupTracksByGroup.js b/src/content/util/groupTracksByGroup.js new file mode 100644 index 00000000..bae2c8c5 --- /dev/null +++ b/src/content/util/groupTracksByGroup.js @@ -0,0 +1,23 @@ +import {empty} from '../../util/sugar.js'; + +export default function groupTracksByGroup(tracks, groups) { + const lists = new Map(groups.map(group => [group, []])); + lists.set('other', []); + + for (const track of tracks) { + const group = groups.find(group => group.albums.includes(track.album)); + if (group) { + lists.get(group).push(track); + } else { + other.get('other').push(track); + } + } + + for (const [key, tracks] of lists.entries()) { + if (empty(tracks)) { + lists.delete(key); + } + } + + return lists; +} diff --git a/src/data/things/album.js b/src/data/things/album.js index 2a188f2d..d371f51f 100644 --- a/src/data/things/album.js +++ b/src/data/things/album.js @@ -1,5 +1,6 @@ import Thing from './thing.js'; +import {empty} from '../../util/sugar.js'; import find from '../../util/find.js'; export class Album extends Thing { @@ -34,12 +35,12 @@ export class Album extends Thing { update: {validate: isDate}, expose: { - dependencies: ['date', 'hasCoverArt'], + dependencies: ['date', 'coverArtistContribsByRef'], transform: (coverArtDate, { + coverArtistContribsByRef, date, - hasCoverArt, }) => - (hasCoverArt + (!empty(coverArtistContribsByRef) ? coverArtDate ?? date ?? null : null), }, @@ -103,7 +104,6 @@ export class Album extends Thing { update: {validate: isDimensions}, }, - hasCoverArt: Thing.common.flag(true), hasTrackArt: Thing.common.flag(true), hasTrackNumbers: Thing.common.flag(true), isListedOnHomepage: Thing.common.flag(true), @@ -123,18 +123,16 @@ export class Album extends Thing { artistContribs: Thing.common.dynamicContribs('artistContribsByRef'), coverArtistContribs: Thing.common.dynamicContribs('coverArtistContribsByRef'), - trackCoverArtistContribs: Thing.common.dynamicContribs( - 'trackCoverArtistContribsByRef' - ), - wallpaperArtistContribs: Thing.common.dynamicContribs( - 'wallpaperArtistContribsByRef' - ), - bannerArtistContribs: Thing.common.dynamicContribs( - 'bannerArtistContribsByRef' - ), + trackCoverArtistContribs: Thing.common.dynamicContribs('trackCoverArtistContribsByRef'), + wallpaperArtistContribs: Thing.common.dynamicContribs('wallpaperArtistContribsByRef'), + bannerArtistContribs: Thing.common.dynamicContribs('bannerArtistContribsByRef'), commentatorArtists: Thing.common.commentatorArtists(), + hasCoverArt: Thing.common.contribsPresent('coverArtistContribsByRef'), + hasWallpaperArt: Thing.common.contribsPresent('wallpaperArtistContribsByRef'), + hasBannerArt: Thing.common.contribsPresent('bannerArtistContribsByRef'), + tracks: { flags: {expose: true}, diff --git a/src/data/things/artist.js b/src/data/things/artist.js index 303f33f3..f144b21f 100644 --- a/src/data/things/artist.js +++ b/src/data/things/artist.js @@ -27,9 +27,8 @@ export class Artist extends Thing { aliasNames: { flags: {update: true, expose: true}, - update: { - validate: validateArrayItems(isName), - }, + update: {validate: validateArrayItems(isName)}, + expose: {transform: (names) => names ?? []}, }, isAlias: Thing.common.flag(), diff --git a/src/data/things/homepage-layout.js b/src/data/things/homepage-layout.js index c18e8110..a79dd77a 100644 --- a/src/data/things/homepage-layout.js +++ b/src/data/things/homepage-layout.js @@ -68,10 +68,10 @@ export class HomepageLayoutAlbumsRow extends HomepageLayoutRow { Group, validators: { + is, isCountingNumber, isString, validateArrayItems, - validateFromConstants, }, } = opts) => ({ ...HomepageLayoutRow[Thing.getPropertyDescriptors](opts), @@ -95,7 +95,7 @@ export class HomepageLayoutAlbumsRow extends HomepageLayoutRow { flags: {update: true, expose: true}, update: { - validate: validateFromConstants('grid', 'carousel'), + validate: is('grid', 'carousel'), }, expose: { diff --git a/src/data/things/language.js b/src/data/things/language.js index 3086ad2e..98ab9bc7 100644 --- a/src/data/things/language.js +++ b/src/data/things/language.js @@ -252,19 +252,19 @@ export class Language extends Thing { // Conjunction list: A, B, and C formatConjunctionList(array) { this.assertIntlAvailable('intl_listConjunction'); - return this.intl_listConjunction.format(array); + return this.intl_listConjunction.format(array.map(arr => arr.toString())); } // Disjunction lists: A, B, or C formatDisjunctionList(array) { this.assertIntlAvailable('intl_listDisjunction'); - return this.intl_listDisjunction.format(array); + return this.intl_listDisjunction.format(array.map(arr => arr.toString())); } // Unit lists: A, B, C formatUnitList(array) { this.assertIntlAvailable('intl_listUnit'); - return this.intl_listUnit.format(array); + return this.intl_listUnit.format(array.map(arr => arr.toString())); } // File sizes: 42.5 kB, 127.2 MB, 4.13 GB, 998.82 TB diff --git a/src/data/things/thing.js b/src/data/things/thing.js index 9c59436e..5004f4e6 100644 --- a/src/data/things/thing.js +++ b/src/data/things/thing.js @@ -23,6 +23,7 @@ import { import {inspect} from 'util'; import {color} from '../../util/cli.js'; +import {empty} from '../../util/sugar.js'; import {getKebabCase} from '../../util/wiki-data.js'; import find from '../../util/find.js'; @@ -63,6 +64,7 @@ export default class Thing extends CacheableObject { urls: () => ({ flags: {update: true, expose: true}, update: {validate: validateArrayItems(isURL)}, + expose: {transform: (value) => value ?? []}, }), // A file extension! Or the default, if provided when calling this. @@ -312,6 +314,20 @@ export default class Thing extends CacheableObject { }, }), + // Nice 'n simple shorthand for an exposed-only flag which is true when any + // contributions are present in the specified property. + contribsPresent: (contribsByRefProperty) => ({ + flags: {expose: true}, + expose: { + dependencies: [contribsByRefProperty], + compute({ + [contribsByRefProperty]: contribsByRef, + }) { + return !empty(contribsByRef); + }, + } + }), + // Neat little shortcut for "reversing" the reference lists stored on other // things - for example, tracks specify a "referenced tracks" property, and // you would use this to compute a corresponding "referenced *by* tracks" diff --git a/src/data/things/validators.js b/src/data/things/validators.js index b116120a..14092102 100644 --- a/src/data/things/validators.js +++ b/src/data/things/validators.js @@ -112,7 +112,12 @@ export function isInstance(value, constructor) { } export function isDate(value) { - return isInstance(value, Date); + isInstance(value, Date); + + if (isNaN(value)) + throw new TypeError(`Expected valid date`); + + return true; } export function isObject(value) { @@ -133,6 +138,34 @@ export function isArray(value) { return true; } +// This one's shaped a bit different from other "is" functions. +// More like validate functions, it returns a function. +export function is(...values) { + if (Array.isArray(values)) { + values = new Set(values); + } + + if (values.size === 1) { + const expected = Array.from(values)[0]; + + return (value) => { + if (value !== expected) { + throw new TypeError(`Expected ${expected}, got ${value}`); + } + + return true; + }; + } + + return (value) => { + if (!values.has(value)) { + throw new TypeError(`Expected one of ${Array.from(values).join(' ')}, got ${value}`); + } + + return true; + }; +} + function validateArrayItemsHelper(itemValidator) { return (item, index) => { try { @@ -162,18 +195,12 @@ export function validateArrayItems(itemValidator) { }; } -export function validateInstanceOf(constructor) { - return (object) => isInstance(object, constructor); +export function arrayOf(itemValidator) { + return validateArrayItems(itemValidator); } -export function validateFromConstants(...values) { - return (value) => { - if (!values.includes(value)) { - throw new TypeError(`Expected one of ${values.join(', ')}`); - } - - return true; - }; +export function validateInstanceOf(constructor) { + return (object) => isInstance(object, constructor); } // Wiki data (primitives & non-primitives) diff --git a/src/data/yaml.js b/src/data/yaml.js index de0b506b..73450f17 100644 --- a/src/data/yaml.js +++ b/src/data/yaml.js @@ -192,7 +192,6 @@ export const processAlbumDocument = makeProcessDocument(T.Album, { color: 'Color', urls: 'URLs', - hasCoverArt: 'Has Cover Art', hasTrackArt: 'Has Track Art', hasTrackNumbers: 'Has Track Numbers', isListedOnHomepage: 'Listed on Homepage', diff --git a/src/misc-templates.js b/src/misc-templates.js index 8f3f0166..dfff4d88 100644 --- a/src/misc-templates.js +++ b/src/misc-templates.js @@ -16,547 +16,8 @@ import { getTotalDuration, sortAlbumsTracksChronologically, sortChronologically, - sortFlashesChronologically, } from './util/wiki-data.js'; -const BANDCAMP_DOMAINS = ['bc.s3m.us', 'music.solatrux.com']; - -const MASTODON_DOMAINS = ['types.pl']; - -// "Additional Files" listing - -function unbound_generateAdditionalFilesShortcut(additionalFiles, { - html, - language, -}) { - if (empty(additionalFiles)) return ''; - - return language.$('releaseInfo.additionalFiles.shortcut', { - anchorLink: - html.tag('a', - {href: '#additional-files'}, - language.$('releaseInfo.additionalFiles.shortcut.anchorLink')), - titles: language.formatUnitList( - additionalFiles.map(g => g.title)), - }); -} - -function unbound_generateAdditionalFilesList(additionalFiles, { - html, - language, - - getFileSize, - linkFile, -}) { - if (empty(additionalFiles)) return []; - - return html.tag('dl', - additionalFiles.flatMap(({title, description, files}) => [ - html.tag('dt', - (description - ? language.$('releaseInfo.additionalFiles.entry.withDescription', { - title, - description, - }) - : language.$('releaseInfo.additionalFiles.entry', {title}))), - - html.tag('dd', - html.tag('ul', - files.map((file) => { - const size = (getFileSize && getFileSize(file)); - return html.tag('li', - (size - ? language.$('releaseInfo.additionalFiles.file.withSize', { - file: linkFile(file), - size: language.formatFileSize(size), - }) - : language.$('releaseInfo.additionalFiles.file', { - file: linkFile(file), - }))) - }))), - ])); -} - -// Artist strings - -function unbound_getArtistString(artists, { - html, - language, - link, - - iconifyURL, - - showIcons = false, - showContrib = false, -}) { - return language.formatConjunctionList( - artists.map(({who, what}) => { - const {urls} = who; - - const hasContribPart = !!(showContrib && what); - const hasExternalPart = !!(showIcons && !empty(urls)); - - const artistLink = link.artist(who); - - const externalLinks = hasExternalPart && - html.tag('span', - { - [html.noEdgeWhitespace]: true, - class: 'icons' - }, - language.formatUnitList( - urls.slice(0, 4).map(url => iconifyURL(url, {language})))); - - return html.tag('span', {class: 'nowrap'}, - (hasContribPart - ? (hasExternalPart - ? language.$('misc.artistLink.withContribution.withExternalLinks', { - artist: artistLink, - contrib: what, - links: externalLinks, - }) - : language.$('misc.artistLink.withContribution', { - artist: artistLink, - contrib: what, - })) - : (hasExternalPart - ? language.$('misc.artistLink.withExternalLinks', { - artist: artistLink, - links: externalLinks, - }) - : language.$('misc.artistLink', { - artist: artistLink, - })))); - })); -} - -// Chronology links - -function unbound_generateChronologyLinks(currentThing, { - html, - language, - link, - - generateNavigationLinks, - - dateKey = 'date', - contribKey, - getThings, - headingString, -}) { - const contributions = currentThing[contribKey]; - - if (empty(contributions)) { - return []; - } - - if (contributions.length > 8) { - return html.tag('div', {class: 'chronology'}, - language.$('misc.chronology.seeArtistPages')); - } - - return contributions - .map(({who: artist}) => { - const thingsUnsorted = unique(getThings(artist)) - .filter((t) => t[dateKey]); - - // Kinda a hack, but we automatically detect which is (probably) the - // right function to use here. - const args = [thingsUnsorted, {getDate: (t) => t[dateKey]}]; - const things = ( - thingsUnsorted.every(t => t instanceof T.Album || t instanceof T.Track) - ? sortAlbumsTracksChronologically(...args) - : thingsUnsorted.every(t => t instanceof T.Flash) - ? sortFlashesChronologically(...args) - : sortChronologically(...args)); - - if (things.length === 0) return ''; - - const index = things.indexOf(currentThing); - - if (index === -1) return ''; - - const heading = ( - html.tag('span', {class: 'heading'}, - language.$(headingString, { - index: language.formatIndex(index + 1, {language}), - artist: link.artist(artist), - }))); - - const navigation = things.length > 1 && - html.tag('span', - { - [html.onlyIfContent]: true, - class: 'buttons', - }, - generateNavigationLinks(currentThing, { - data: things, - isMain: false, - })); - - return ( - html.tag('div', {class: 'chronology'}, - (navigation - ? language.$('misc.chronology.withNavigation', { - heading, - navigation, - }) - : heading))); - }); -} - -// Content warning tags - -function unbound_getRevealStringFromContentWarningMessage(warnings, { - html, - language, -}) { - return ( - language.$('misc.contentWarnings', {warnings}) + - html.tag('br') + - html.tag('span', {class: 'reveal-interaction'}, - language.$('misc.contentWarnings.reveal')) - ); -} - -function unbound_getRevealStringFromArtTags(tags, { - getRevealStringFromContentWarningMessage, - language, -}) { - return ( - tags?.some(tag => tag.isContentWarning) && - getRevealStringFromContentWarningMessage( - language.formatUnitList( - tags - .filter(tag => tag.isContentWarning) - .map(tag => tag.name))) - ); -} - -// Cover art links - -function unbound_generateCoverLink({ - html, - img, - language, - link, - - getRevealStringFromArtTags, - - alt, - path, - src, - tags = [], - to, - wikiData, -}) { - const {wikiInfo} = wikiData; - - if (!src && path) { - src = to(...path); - } - - if (!src) { - throw new Error(`Expected src or path`); - } - - const linkedTags = tags.filter(tag => !tag.isContentWarning); - - return html.tag('div', {id: 'cover-art-container'}, [ - img({ - src, - alt, - thumb: 'medium', - id: 'cover-art', - link: true, - square: true, - reveal: getRevealStringFromArtTags(tags), - }), - - wikiInfo.enableArtTagUI && - linkedTags.length && - html.tag('p', {class: 'tags'}, - language.$('releaseInfo.artTags.inline', { - tags: language.formatUnitList( - linkedTags.map(tag => link.tag(tag))), - })), - ]); -} - -// CSS & color shenanigans - -function unbound_getThemeString(color, { - getColors, - - additionalVariables = [], -} = {}) { - if (!color) return ''; - - const { - primary, - dark, - dim, - dimGhost, - bg, - bgBlack, - shadow, - } = getColors(color); - - const variables = [ - `--primary-color: ${primary}`, - `--dark-color: ${dark}`, - `--dim-color: ${dim}`, - `--dim-ghost-color: ${dimGhost}`, - `--bg-color: ${bg}`, - `--bg-black-color: ${bgBlack}`, - `--shadow-color: ${shadow}`, - ...additionalVariables, - ].filter(Boolean); - - if (!variables.length) return ''; - - return [ - `:root {`, - ...variables.map((line) => ` ${line};`), - `}` - ].join('\n'); -} - -function unbound_getAlbumStylesheet(album, { - to, -}) { - const hasWallpaper = album.wallpaperArtistContribs.length >= 1; - const hasWallpaperStyle = !!album.wallpaperStyle; - const hasBannerStyle = !!album.bannerStyle; - - const wallpaperSource = - (hasWallpaper && - to( - 'media.albumWallpaper', - album.directory, - album.wallpaperFileExtension)); - - const wallpaperPart = - (hasWallpaper - ? [ - `body::before {`, - ` background-image: url("${wallpaperSource}");`, - ...(hasWallpaperStyle - ? album.wallpaperStyle - .split('\n') - .map(line => ` ${line}`) - : []), - `}`, - ] - : []); - - const bannerPart = - (hasBannerStyle - ? [ - `#banner img {`, - ...album.bannerStyle - .split('\n') - .map(line => ` ${line}`), - `}`, - ] - : []); - - return [ - ...wallpaperPart, - ...bannerPart, - ] - .filter(Boolean) - .join('\n'); -} - -// Divided track lists - -function unbound_generateTrackListDividedByGroups(tracks, { - html, - language, - - getTrackItem, - wikiData, -}) { - const {divideTrackListsByGroups: groups} = wikiData.wikiInfo; - - if (empty(groups)) { - return html.tag('ul', - tracks.map(t => getTrackItem(t))); - } - - const lists = Object.fromEntries( - groups.map((group) => [ - group.directory, - {group, tracks: []} - ])); - - const other = []; - - for (const track of tracks) { - const {album} = track; - const group = groups.find((g) => g.albums.includes(album)); - if (group) { - lists[group.directory].tracks.push(track); - } else { - other.push(track); - } - } - - const dt = name => - html.tag('dt', - language.$('trackList.group', { - group: name, - })); - - const ddul = tracks => - html.tag('dd', - html.tag('ul', - tracks.map(t => getTrackItem(t)))); - - return html.tag('dl', [ - ...Object.values(lists) - .filter(({tracks}) => tracks.length) - .flatMap(({group, tracks}) => [ - dt(group.name), - ddul(tracks), - ]), - - ...html.fragment( - other.length && [ - dt(language.$('trackList.group.other')), - ddul(other), - ]), - ]); -} - -// Fancy lookin' links - -function unbound_fancifyURL(url, { - html, - language, - - album = false, -} = {}) { - let local = Symbol(); - let domain; - try { - domain = new URL(url).hostname; - } catch (error) { - // No support for relative local URLs yet, sorry! (I.e, local URLs must - // be absolute relative to the domain name in order to work.) - domain = local; - } - - return html.tag('a', - { - href: url, - class: 'nowrap', - }, - - // truly unhinged indentation here - domain === local - ? language.$('misc.external.local') - : domain.includes('bandcamp.com') - ? language.$('misc.external.bandcamp') - : BANDCAMP_DOMAINS.includes(domain) - ? language.$('misc.external.bandcamp.domain', {domain}) - : MASTODON_DOMAINS.includes(domain) - ? language.$('misc.external.mastodon.domain', {domain}) - : domain.includes('youtu') - ? album - ? url.includes('list=') - ? language.$('misc.external.youtube.playlist') - : language.$('misc.external.youtube.fullAlbum') - : language.$('misc.external.youtube') - : domain.includes('soundcloud') - ? language.$('misc.external.soundcloud') - : domain.includes('tumblr.com') - ? language.$('misc.external.tumblr') - : domain.includes('twitter.com') - ? language.$('misc.external.twitter') - : domain.includes('deviantart.com') - ? language.$('misc.external.deviantart') - : domain.includes('wikipedia.org') - ? language.$('misc.external.wikipedia') - : domain.includes('poetryfoundation.org') - ? language.$('misc.external.poetryFoundation') - : domain.includes('instagram.com') - ? language.$('misc.external.instagram') - : domain.includes('patreon.com') - ? language.$('misc.external.patreon') - : domain.includes('spotify.com') - ? language.$('misc.external.spotify') - : domain.includes('newgrounds.com') - ? language.$('misc.external.newgrounds') - : domain); -} - -function unbound_fancifyFlashURL(url, flash, { - html, - language, - - fancifyURL, -}) { - const link = fancifyURL(url); - return html.tag('span', - {class: 'nowrap'}, - url.includes('homestuck.com') - ? isNaN(Number(flash.page)) - ? language.$('misc.external.flash.homestuck.secret', {link}) - : language.$('misc.external.flash.homestuck.page', { - link, - page: flash.page, - }) - : url.includes('bgreco.net') - ? language.$('misc.external.flash.bgreco', {link}) - : url.includes('youtu') - ? language.$('misc.external.flash.youtube', {link}) - : link); -} - -function unbound_iconifyURL(url, { - html, - language, - to, -}) { - const domain = new URL(url).hostname; - const [id, msg] = ( - domain.includes('bandcamp.com') - ? ['bandcamp', language.$('misc.external.bandcamp')] - : BANDCAMP_DOMAINS.includes(domain) - ? ['bandcamp', language.$('misc.external.bandcamp.domain', {domain})] - : MASTODON_DOMAINS.includes(domain) - ? ['mastodon', language.$('misc.external.mastodon.domain', {domain})] - : domain.includes('youtu') - ? ['youtube', language.$('misc.external.youtube')] - : domain.includes('soundcloud') - ? ['soundcloud', language.$('misc.external.soundcloud')] - : domain.includes('tumblr.com') - ? ['tumblr', language.$('misc.external.tumblr')] - : domain.includes('twitter.com') - ? ['twitter', language.$('misc.external.twitter')] - : domain.includes('deviantart.com') - ? ['deviantart', language.$('misc.external.deviantart')] - : domain.includes('instagram.com') - ? ['instagram', language.$('misc.external.bandcamp')] - : domain.includes('newgrounds.com') - ? ['newgrounds', language.$('misc.external.newgrounds')] - : ['globe', language.$('misc.external.domain', {domain})]); - - return html.tag('a', - { - href: url, - class: 'icon', - }, - html.tag('svg', [ - html.tag('title', msg), - html.tag('use', { - href: to('shared.staticFile', `icons.svg#icon-${id}`), - }), - ])); -} - // Grids function unbound_getGridHTML({ @@ -636,123 +97,6 @@ function unbound_getFlashGridHTML({ }); } -// Images - -function unbound_img({ - getSizeOfImageFile, - html, - to, - - src, - alt, - noSrcText = '', - thumb: thumbKey, - reveal, - id, - class: className, - width, - height, - link = false, - lazy = false, - square = false, -}) { - const willSquare = square; - const willLink = typeof link === 'string' || link; - - const originalSrc = src; - const thumbSrc = src && (thumbKey ? thumb[thumbKey](src) : src); - - const href = - (willLink - ? (typeof link === 'string' - ? link - : originalSrc) - : null); - - let fileSize = null; - const mediaRoot = to('media.root'); - if (href?.startsWith(mediaRoot)) { - fileSize = getSizeOfImageFile(href.slice(mediaRoot.length).replace(/^\//, '')); - } - - const imgAttributes = { - id: link ? '' : id, - class: className, - alt, - width, - height, - 'data-original-size': fileSize, - }; - - const noSrcHTML = - !src && - wrap( - html.tag('div', - {class: 'image-text-area'}, - noSrcText)); - - const nonlazyHTML = - src && - wrap( - html.tag('img', { - ...imgAttributes, - src: thumbSrc, - })); - - const lazyHTML = - src && - lazy && - wrap( - html.tag('img', - { - ...imgAttributes, - class: [className, 'lazy'], - 'data-original': thumbSrc, - }), - true); - - if (!src) { - return noSrcHTML; - } else if (lazy) { - return html.tag('noscript', nonlazyHTML) + '\n' + lazyHTML; - } else { - return nonlazyHTML; - } - - function wrap(input, hide = false) { - let wrapped = input; - - wrapped = html.tag('div', {class: 'image-container'}, wrapped); - - if (reveal) { - wrapped = html.tag('div', {class: 'reveal'}, [ - wrapped, - html.tag('span', {class: 'reveal-text-container'}, - html.tag('span', {class: 'reveal-text'}, reveal)), - ]); - } - - if (willSquare) { - wrapped = html.tag('div', {class: 'square-content'}, wrapped); - wrapped = html.tag('div', - {class: ['square', hide && !willLink && 'js-hide']}, - wrapped); - } - - if (willLink) { - wrapped = html.tag('a', - { - id, - class: ['box', hide && 'js-hide', 'image-link'], - href, - }, - wrapped); - } - - return wrapped; - } -} - // Carousel reels // Layout constants: @@ -851,228 +195,11 @@ function unbound_getCarouselHTML({ })))))); } -// Nav-bar links - -function unbound_generateInfoGalleryLinks(currentThing, isGallery, { - link, - language, - - linkKeyGallery, - linkKeyInfo, -}) { - return [ - link[linkKeyInfo](currentThing, { - class: isGallery ? '' : 'current', - text: language.$('misc.nav.info'), - }), - link[linkKeyGallery](currentThing, { - class: isGallery ? 'current' : '', - text: language.$('misc.nav.gallery'), - }), - ].join(', '); -} - -// Generate "previous" and "next" links relative to a given current thing and a -// data set (array of things) which includes it, optionally including additional -// provided links like "random". This is for use in navigation bars and other -// inline areas. -// -// By default, generated links include ID attributes which enable client-side -// keyboard shortcuts. Provide isMain: false to disable this (if the generated -// links aren't the for the page's primary navigation). -function unbound_generateNavigationLinks(current, { - language, - link, - - additionalLinks = [], - data, - isMain = true, - linkKey = 'anything', - returnAsArray = false, -}) { - let previousLink, nextLink; - - if (current) { - const linkFn = link[linkKey].bind(link); - - const index = data.indexOf(current); - const previousThing = data[index - 1]; - const nextThing = data[index + 1]; - - previousLink = previousThing && - linkFn(previousThing, { - attributes: { - id: isMain && 'previous-button', - title: previousThing.name, - }, - text: language.$('misc.nav.previous'), - color: false, - }); - - nextLink = nextThing && - linkFn(nextThing, { - attributes: { - id: isMain && 'next-button', - title: nextThing.name, - }, - text: language.$('misc.nav.next'), - color: false, - }); - } - - const links = [ - previousLink, - nextLink, - ...additionalLinks, - ].filter(Boolean); - - if (returnAsArray) { - return links; - } else if (empty(links)) { - return ''; - } else { - return language.formatUnitList(links); - } -} - -// Sticky heading, ooooo - -function unbound_generateContentHeading({ - html, - - id, - title, -}) { - return html.tag('p', - { - class: 'content-heading', - id, - tabindex: '0', - }, - title); -} - -function unbound_generateStickyHeadingContainer({ - html, - img, - - class: classes, - coverSrc, - coverAlt, - coverArtTags, - title, -}) { - return html.tag('div', - {class: [ - 'content-sticky-heading-container', - coverSrc && 'has-cover', - ].concat(classes)}, - [ - html.tag('div', {class: 'content-sticky-heading-row'}, [ - html.tag('h1', title), - - // Cover art in the sticky heading never uses the 'reveal' setting - // because it's too small to effectively display content warnings. - // Instead, if art has content warnings, it's hidden from the sticky - // heading by default, and will be enabled once the main cover art - // is revealed. - coverSrc && - html.tag('div', {class: 'content-sticky-heading-cover-container'}, - html.tag('div', - { - class: [ - 'content-sticky-heading-cover', - coverArtTags.some(tag => tag.isContentWarning) && - 'content-sticky-heading-cover-needs-reveal', - ], - }, - img({ - src: coverSrc, - alt: coverAlt, - thumb: 'small', - link: false, - square: true, - }))), - ]), - - html.tag('div', {class: 'content-sticky-subheading-row'}, - html.tag('h2', {class: 'content-sticky-subheading'})), - ]); -} - -// Footer stuff - -function unbound_getFooterLocalizationLinks({ - html, - defaultLanguage, - language, - languages, - pagePath, - to, -}) { - const links = Object.entries(languages) - .filter(([code, language]) => code !== 'default' && !language.hidden) - .map(([code, language]) => language) - .sort(({name: a}, {name: b}) => (a < b ? -1 : a > b ? 1 : 0)) - .map((language) => - html.tag('span', - html.tag('a', - { - href: - language === defaultLanguage - ? to( - 'localizedDefaultLanguage.' + pagePath[0], - ...pagePath.slice(1)) - : to( - 'localizedWithBaseDirectory.' + pagePath[0], - language.code, - ...pagePath.slice(1)), - }, - language.name))); - - return html.tag('div', {class: 'footer-localization-links'}, - language.$('misc.uiLanguage', { - languages: links.join('\n'), - })); -} - // Exports export { - unbound_generateAdditionalFilesList as generateAdditionalFilesList, - unbound_generateAdditionalFilesShortcut as generateAdditionalFilesShortcut, - - unbound_getArtistString as getArtistString, - - unbound_generateChronologyLinks as generateChronologyLinks, - - unbound_getRevealStringFromContentWarningMessage as getRevealStringFromContentWarningMessage, - unbound_getRevealStringFromArtTags as getRevealStringFromArtTags, - - unbound_generateCoverLink as generateCoverLink, - - unbound_getThemeString as getThemeString, - unbound_getAlbumStylesheet as getAlbumStylesheet, - - unbound_generateTrackListDividedByGroups as generateTrackListDividedByGroups, - - unbound_fancifyURL as fancifyURL, - unbound_fancifyFlashURL as fancifyFlashURL, - unbound_iconifyURL as iconifyURL, - unbound_getGridHTML as getGridHTML, unbound_getAlbumGridHTML as getAlbumGridHTML, unbound_getFlashGridHTML as getFlashGridHTML, - unbound_getCarouselHTML as getCarouselHTML, - - unbound_img as img, - - unbound_generateInfoGalleryLinks as generateInfoGalleryLinks, - unbound_generateNavigationLinks as generateNavigationLinks, - - unbound_generateContentHeading as generateContentHeading, - unbound_generateStickyHeadingContainer as generateStickyHeadingContainer, - - unbound_getFooterLocalizationLinks as getFooterLocalizationLinks, } diff --git a/src/page/album.js b/src/page/album.js index 9ee57c09..111cab82 100644 --- a/src/page/album.js +++ b/src/page/album.js @@ -1,66 +1,62 @@ // Album page specification. -import { - bindOpts, - compareArrays, - empty, -} from '../util/sugar.js'; - -import { - getAlbumCover, - getAlbumListTag, - getTotalDuration, -} from '../util/wiki-data.js'; - export const description = `per-album info & track artwork gallery pages`; export function targets({wikiData}) { return wikiData.albumData; } -export function write(album, {wikiData}) { - const unbound_trackToListItem = (track, { - getArtistString, - getLinkThemeString, - html, - language, - link, - }) => { - const itemOpts = { - duration: language.formatDuration(track.duration ?? 0), - track: link.track(track), - }; +export function pathsForTarget(album) { + const hasGalleryPage = album.tracks.some(t => t.hasUniqueCoverArt); + const hasCommentaryPage = !!album.commentary || album.tracks.some(t => t.commentary); - return html.tag('li', - {style: getLinkThemeString(track.color)}, - compareArrays( - track.artistContribs.map((c) => c.who), - album.artistContribs.map((c) => c.who), - {checkOrder: false} - ) - ? language.$('trackList.item.withDuration', itemOpts) - : language.$('trackList.item.withDuration.withArtists', { - ...itemOpts, - by: html.tag('span', - {class: 'by'}, - language.$('trackList.item.withArtists.by', { - artists: getArtistString(track.artistContribs), - })), - })); - }; + return [ + { + type: 'page', + path: ['album', album.directory], - const hasAdditionalFiles = !empty(album.additionalFiles); - const numAdditionalFiles = album.additionalFiles.flatMap((g) => g.files).length; + contentFunction: { + name: 'generateAlbumInfoPage', + args: [album], + }, + }, - const albumDuration = getTotalDuration(album.tracks); + /* + hasGalleryPage && { + type: 'page', + path: ['albumGallery', album.directory], - const displayTrackSections = - album.trackSections && - (album.trackSections.length > 1 || - !album.trackSections[0]?.isDefaultTrackSection); + contentFunction: { + name: 'generateAlbumGalleryPage', + args: [album], + }, + }, + + hasCommentaryPage && { + type: 'page', + path: ['albumCommentary', album.directory], + + contentFunction: { + name: 'generateAlbumCommentaryPage', + args: [album], + }, + }, - const listTag = getAlbumListTag(album); + { + type: 'data', + path: ['album', album.directory], + contentFunction: { + name: 'generateAlbumDataFile', + args: [album], + }, + }, + */ + ]; +} + +/* +export function write(album, {wikiData}) { const getSocialEmbedDescription = ({ getArtistString: _getArtistString, language, @@ -123,297 +119,6 @@ export function write(album, {wikiData}) { }), }; - const infoPage = { - type: 'page', - path: ['album', album.directory], - page: ({ - absoluteTo, - fancifyURL, - generateAdditionalFilesShortcut, - generateAdditionalFilesList, - generateChronologyLinks, - generateContentHeading, - generateNavigationLinks, - getAlbumCover, - getAlbumStylesheet, - getArtistString, - getLinkThemeString, - getSizeOfAdditionalFile, - getThemeString, - html, - link, - language, - transformMultiline, - urls, - }) => { - const trackToListItem = bindOpts(unbound_trackToListItem, { - getArtistString, - getLinkThemeString, - html, - language, - link, - }); - - return { - title: language.$('albumPage.title', {album: album.name}), - stylesheet: getAlbumStylesheet(album), - - themeColor: album.color, - theme: - getThemeString(album.color, { - additionalVariables: [ - `--album-directory: ${album.directory}`, - ], - }), - - socialEmbed: { - heading: - (empty(album.groups) - ? '' - : language.$('albumPage.socialEmbed.heading', { - group: album.groups[0].name, - })), - headingLink: - (empty(album.groups) - ? null - : absoluteTo('localized.album', album.groups[0].directory)), - title: language.$('albumPage.socialEmbed.title', { - album: album.name, - }), - description: getSocialEmbedDescription({getArtistString, language}), - image: '/' + getAlbumCover(album, {to: urls.from('shared.root').to}), - color: album.color, - }, - - banner: !empty(album.bannerArtistContribs) && { - dimensions: album.bannerDimensions, - path: [ - 'media.albumBanner', - album.directory, - album.bannerFileExtension, - ], - alt: language.$('misc.alt.albumBanner'), - position: 'top', - }, - - cover: { - src: getAlbumCover(album), - alt: language.$('misc.alt.albumCover'), - artTags: album.artTags, - }, - - main: { - headingMode: 'sticky', - - content: [ - html.tag('p', - { - [html.onlyIfContent]: true, - [html.joinChildren]: '<br>', - }, - [ - !empty(album.artistContribs) && - language.$('releaseInfo.by', { - artists: getArtistString(album.artistContribs, { - showContrib: true, - showIcons: true, - }), - }), - - !empty(album.coverArtistContribs) && - language.$('releaseInfo.coverArtBy', { - artists: getArtistString(album.coverArtistContribs, { - showContrib: true, - showIcons: true, - }), - }), - - !empty(album.wallpaperArtistContribs) && - language.$('releaseInfo.wallpaperArtBy', { - artists: getArtistString(album.wallpaperArtistContribs, { - showContrib: true, - showIcons: true, - }), - }), - - !empty(album.bannerArtistContribs) && - language.$('releaseInfo.bannerArtBy', { - artists: getArtistString(album.bannerArtistContribs, { - showContrib: true, - showIcons: true, - }), - }), - - album.date && - language.$('releaseInfo.released', { - date: language.formatDate(album.date), - }), - - album.hasCoverArt && - album.coverArtDate && - +album.coverArtDate !== +album.date && - language.$('releaseInfo.artReleased', { - date: language.formatDate(album.coverArtDate), - }), - - albumDuration > 0 && - language.$('releaseInfo.duration', { - duration: language.formatDuration(albumDuration, { - approximate: album.tracks.length > 1, - }), - }), - ]), - - html.tag('p', - { - [html.onlyIfContent]: true, - [html.joinChildren]: '<br>', - }, - [ - hasAdditionalFiles && - generateAdditionalFilesShortcut(album.additionalFiles), - - checkGalleryPage(album) && - language.$('releaseInfo.viewGallery', { - link: link.albumGallery(album, { - text: language.$('releaseInfo.viewGallery.link'), - }), - }), - - checkCommentaryPage(album) && - language.$('releaseInfo.viewCommentary', { - link: link.albumCommentary(album, { - text: language.$('releaseInfo.viewCommentary.link'), - }), - }), - ]), - - !empty(album.urls) && - html.tag('p', - language.$('releaseInfo.listenOn', { - links: language.formatDisjunctionList( - album.urls.map(url => fancifyURL(url, {album: true})) - ), - })), - - displayTrackSections && - !empty(album.trackSections) && - html.tag('dl', - {class: 'album-group-list'}, - album.trackSections.flatMap(({ - name, - startIndex, - tracks, - }) => [ - html.tag('dt', - {class: ['content-heading']}, - language.$('trackList.section.withDuration', { - duration: language.formatDuration(getTotalDuration(tracks), { - approximate: tracks.length > 1, - }), - section: name, - })), - html.tag('dd', - html.tag(listTag, - listTag === 'ol' ? {start: startIndex + 1} : {}, - tracks.map(trackToListItem))), - ])), - - !displayTrackSections && - !empty(album.tracks) && - html.tag(listTag, - album.tracks.map(trackToListItem)), - - html.tag('p', - { - [html.onlyIfContent]: true, - [html.joinChildren]: '<br>', - }, - [ - album.dateAddedToWiki && - language.$('releaseInfo.addedToWiki', { - date: language.formatDate( - album.dateAddedToWiki - ), - }) - ]), - - ...html.fragment( - hasAdditionalFiles && [ - generateContentHeading({ - id: 'additional-files', - title: language.$('releaseInfo.additionalFiles.heading', { - additionalFiles: language.countAdditionalFiles(numAdditionalFiles, { - unit: true, - }), - }), - }), - - generateAlbumAdditionalFilesList(album, album.additionalFiles, { - generateAdditionalFilesList, - getSizeOfAdditionalFile, - link, - urls, - }), - ]), - - ...html.fragment( - album.commentary && [ - generateContentHeading({ - id: 'artist-commentary', - title: language.$('releaseInfo.artistCommentary'), - }), - - html.tag('blockquote', transformMultiline(album.commentary)), - ]), - ], - }, - - sidebarLeft: generateAlbumSidebar(album, null, { - fancifyURL, - getLinkThemeString, - html, - link, - language, - transformMultiline, - wikiData, - }), - - nav: { - linkContainerClasses: ['nav-links-hierarchy'], - links: [ - {toHome: true}, - { - html: language.$('albumPage.nav.album', { - album: link.album(album, {class: 'current'}), - }), - }, - { - divider: false, - html: generateAlbumNavLinks(album, null, { - generateNavigationLinks, - html, - language, - link, - }), - } - ], - content: generateAlbumChronologyLinks(album, null, { - generateChronologyLinks, - html, - }), - }, - - secondaryNav: generateAlbumSecondaryNav(album, null, { - getLinkThemeString, - html, - language, - link, - }), - }; - }, - }; - // TODO: only gen if there are any tracks with art const galleryPage = { type: 'page', @@ -494,153 +199,6 @@ export function write(album, {wikiData}) { }), }), }; - - return [ - infoPage, - galleryPage, - data, - ]; -} - -// Utility functions - -export function generateAlbumSidebar(album, currentTrack, { - fancifyURL, - getLinkThemeString, - html, - language, - link, - transformMultiline, -}) { - const isAlbumPage = !currentTrack; - const isTrackPage = !!currentTrack; - - const listTag = getAlbumListTag(album); - - const {trackSections} = album; - - const trackToListItem = (track) => - html.tag('li', - {class: track === currentTrack && 'current'}, - language.$('albumSidebar.trackList.item', { - track: link.track(track), - })); - - const nameOrDefault = (isDefaultTrackSection, name) => - isDefaultTrackSection - ? language.$('albumSidebar.trackList.fallbackSectionName') - : name; - - const trackListPart = [ - html.tag('h1', link.album(album)), - ...trackSections.map(({name, color, startIndex, tracks, isDefaultTrackSection}) => { - const groupName = - html.tag('span', - {class: 'group-name'}, - nameOrDefault( - isDefaultTrackSection, - name - )); - return html.tag('details', - { - // Leave side8ar track groups collapsed on al8um homepage, - // since there's already a view of all the groups expanded - // in the main content area. - open: isTrackPage && tracks.includes(currentTrack), - class: tracks.includes(currentTrack) && 'current', - }, - [ - html.tag( - 'summary', - {style: getLinkThemeString(color)}, - html.tag('span', [ - listTag === 'ol' && - language.$('albumSidebar.trackList.group.withRange', { - group: groupName, - range: `${startIndex + 1}–${ - startIndex + tracks.length - }`, - }), - listTag === 'ul' && - language.$('albumSidebar.trackList.group', { - group: groupName, - }), - ])), - html.tag(listTag, - listTag === 'ol' ? {start: startIndex + 1} : {}, - tracks.map(trackToListItem)), - ]); - }), - ]; - - const {groups} = album; - - const groupParts = groups - .map((group) => { - const albums = group.albums.filter((album) => album.date); - const index = albums.indexOf(album); - const next = index >= 0 && albums[index + 1]; - const previous = index > 0 && albums[index - 1]; - return {group, next, previous}; - }) - // This is a map and not a flatMap because the distinction between which - // group sets of elements belong to matters. That means this variable is an - // array of arrays, and we'll need to treat it as such later! - .map(({group, next, previous}) => [ - html.tag('h1', language.$('albumSidebar.groupBox.title', { - group: link.groupInfo(group), - })), - - isAlbumPage && - transformMultiline(group.descriptionShort), - - !empty(group.urls) && - html.tag('p', language.$('releaseInfo.visitOn', { - links: language.formatDisjunctionList( - group.urls.map((url) => fancifyURL(url)) - ), - })), - - ...html.fragment( - isAlbumPage && [ - next && - html.tag('p', - {class: 'group-chronology-link'}, - language.$('albumSidebar.groupBox.next', { - album: link.album(next), - })), - - previous && - html.tag('p', - {class: 'group-chronology-link'}, - language.$('albumSidebar.groupBox.previous', { - album: link.album(previous), - })), - ]), - ]); - - if (empty(groupParts)) { - return { - stickyMode: 'column', - content: trackListPart, - }; - } else if (isTrackPage) { - const combinedGroupPart = { - classes: ['no-sticky-header'], - content: groupParts - .map(groupPart => groupPart.filter(Boolean).join('\n')) - .join('\n<hr>\n'), - }; - return { - stickyMode: 'column', - multiple: [trackListPart, combinedGroupPart], - }; - } else { - return { - stickyMode: 'last', - multiple: [...groupParts, trackListPart], - }; - } } export function generateAlbumSecondaryNav(album, currentTrack, { @@ -696,174 +254,4 @@ export function generateAlbumSecondaryNav(album, currentTrack, { content: groupParts, }; } - -function checkGalleryPage(album) { - return album.tracks.some(t => t.hasUniqueCoverArt); -} - -function checkCommentaryPage(album) { - return !!album.commentary || album.tracks.some(t => t.commentary); -} - -export function generateAlbumNavLinks(album, currentTrack, { - generateNavigationLinks, - html, - language, - link, - - currentExtra = null, - showTrackNavigation = true, - showExtraLinks = null, -}) { - const isTrackPage = !!currentTrack; - - showExtraLinks ??= currentTrack ? false : true; - - const extraLinks = showExtraLinks ? [ - checkGalleryPage(album) && - link.albumGallery(album, { - class: [currentExtra === 'gallery' && 'current'], - text: language.$('albumPage.nav.gallery'), - }), - - checkCommentaryPage(album) && - link.albumCommentary(album, { - class: [currentExtra === 'commentary' && 'current'], - text: language.$('albumPage.nav.commentary'), - }), - ].filter(Boolean) : []; - - const previousNextLinks = - showTrackNavigation && - album.tracks.length > 1 && - generateNavigationLinks(currentTrack, { - data: album.tracks, - linkKey: 'track', - returnAsArray: true, - }) - - const randomLink = - showTrackNavigation && - album.tracks.length > 1 && - html.tag('a', - { - href: '#', - 'data-random': 'track-in-album', - id: 'random-button' - }, - (isTrackPage - ? language.$('trackPage.nav.random') - : language.$('albumPage.nav.randomTrack'))); - - const allLinks = [ - ...previousNextLinks || [], - ...extraLinks || [], - randomLink, - ].filter(Boolean); - - if (empty(allLinks)) { - return ''; - } - - return `(${language.formatUnitList(allLinks)})`; -} - -export function generateAlbumExtrasPageNav(album, currentExtra, { - html, - language, - link, -}) { - return { - linkContainerClasses: ['nav-links-hierarchy'], - links: [ - {toHome: true}, - { - html: language.$('albumPage.nav.album', { - album: link.album(album, {class: 'current'}), - }), - }, - { - divider: false, - html: generateAlbumNavLinks(album, null, { - currentExtra, - showTrackNavigation: false, - showExtraLinks: true, - - html, - language, - link, - }), - } - ], - }; -} - -export function generateAlbumChronologyLinks(album, currentTrack, { - generateChronologyLinks, - html, -}) { - return html.tag( - 'div', - { - [html.onlyIfContent]: true, - class: 'nav-chronology-links', - }, - [ - ...html.fragment( - currentTrack && [ - ...html.fragment( - generateChronologyLinks(currentTrack, { - contribKey: 'artistContribs', - getThings: (artist) => [ - ...artist.tracksAsArtist, - ...artist.tracksAsContributor, - ], - headingString: 'misc.chronology.heading.track', - })), - - ...html.fragment( - generateChronologyLinks(currentTrack, { - contribKey: 'contributorContribs', - getThings: (artist) => [ - ...artist.tracksAsArtist, - ...artist.tracksAsContributor, - ], - headingString: 'misc.chronology.heading.track', - })), - ]), - - ...html.fragment( - generateChronologyLinks(currentTrack || album, { - contribKey: 'coverArtistContribs', - dateKey: 'coverArtDate', - getThings: (artist) => [ - ...artist.albumsAsCoverArtist, - ...artist.tracksAsCoverArtist, - ], - headingString: 'misc.chronology.heading.coverArt', - })), - ]); -} - -export function generateAlbumAdditionalFilesList(album, additionalFiles, { - fileSize = true, - - generateAdditionalFilesList, - getSizeOfAdditionalFile, - link, - urls, -}) { - return generateAdditionalFilesList(additionalFiles, { - getFileSize: - (fileSize - ? (file) => - // TODO: Kinda near the metal here... - getSizeOfAdditionalFile( - urls - .from('media.root') - .to('media.albumAdditionalFile', album.directory, file)) - : () => null), - linkFile: (file) => - link.albumAdditionalFile({album, file}), - }); -} +*/ diff --git a/src/page/artist.js b/src/page/artist.js index 4ef44d32..ad365161 100644 --- a/src/page/artist.js +++ b/src/page/artist.js @@ -2,682 +2,22 @@ // // NB: See artist-alias.js for artist alias redirect pages. -import { - bindOpts, - empty, - unique, -} from '../util/sugar.js'; - -import { - chunkByProperties, - getTotalDuration, - sortAlbumsTracksChronologically, - sortFlashesChronologically, -} from '../util/wiki-data.js'; - export const description = `per-artist info & artwork gallery pages`; export function targets({wikiData}) { return wikiData.artistData; } -export function write(artist, {wikiData}) { - const {groupData, wikiInfo} = wikiData; - - const {name, urls, contextNotes} = artist; - - const artThingsAll = sortAlbumsTracksChronologically( - unique([ - ...(artist.albumsAsCoverArtist ?? []), - ...(artist.albumsAsWallpaperArtist ?? []), - ...(artist.albumsAsBannerArtist ?? []), - ...(artist.tracksAsCoverArtist ?? []), - ]), - {getDate: (o) => o.coverArtDate}); - - const artThingsGallery = sortAlbumsTracksChronologically( - [ - ...(artist.albumsAsCoverArtist ?? []), - ...(artist.tracksAsCoverArtist ?? []), - ], - {latestFirst: true, getDate: (o) => o.coverArtDate}); - - const commentaryThings = sortAlbumsTracksChronologically([ - ...(artist.albumsAsCommentator ?? []), - ...(artist.tracksAsCommentator ?? []), - ]); - - const hasGallery = !empty(artThingsGallery); - - const getArtistsAndContrib = (thing, key) => ({ - artists: thing[key]?.filter(({who}) => who !== artist), - contrib: thing[key]?.find(({who}) => who === artist), - thing, - key, - }); - - const artListChunks = chunkByProperties( - artThingsAll.flatMap((thing) => - ['coverArtistContribs', 'wallpaperArtistContribs', 'bannerArtistContribs'] - .map((key) => getArtistsAndContrib(thing, key)) - .filter(({contrib}) => contrib) - .map((props) => ({ - album: thing.album || thing, - track: thing.album ? thing : null, - date: thing.date, - ...props, - }))), - ['date', 'album']); - - const commentaryListChunks = chunkByProperties( - commentaryThings.map((thing) => ({ - album: thing.album || thing, - track: thing.album ? thing : null, - })), - ['album']); - - const allTracks = sortAlbumsTracksChronologically( - unique([ - ...(artist.tracksAsArtist ?? []), - ...(artist.tracksAsContributor ?? []), - ])); - - const chunkTracks = (tracks) => - chunkByProperties( - tracks.map((track) => ({ - track, - date: +track.date, - album: track.album, - duration: track.duration, - originalReleaseTrack: track.originalReleaseTrack, - artists: track.artistContribs.some(({who}) => who === artist) - ? track.artistContribs.filter(({who}) => who !== artist) - : track.contributorContribs.filter(({who}) => who !== artist), - contrib: { - who: artist, - whatArray: [ - track.artistContribs.find(({who}) => who === artist)?.what, - track.contributorContribs.find(({who}) => who === artist)?.what, - ].filter(Boolean), - }, - })), - ['date', 'album']) - .map(({date, album, chunk}) => ({ - date, - album, - chunk, - duration: getTotalDuration(chunk, {originalReleasesOnly: true}), - })); - - const trackListChunks = chunkTracks(allTracks); - const totalDuration = getTotalDuration(allTracks.filter(t => !t.originalReleaseTrack)); - - const countGroups = (things) => { - const usedGroups = things.flatMap( - (thing) => thing.groups || thing.album?.groups || []); - return groupData - .map((group) => ({ - group, - contributions: usedGroups.filter(g => g === group).length, - })) - .filter(({contributions}) => contributions > 0) - .sort((a, b) => b.contributions - a.contributions); - }; - - const musicGroups = countGroups(allTracks); - const artGroups = countGroups(artThingsAll); - - let flashes, flashListChunks; - if (wikiInfo.enableFlashesAndGames) { - flashes = sortFlashesChronologically(artist.flashesAsContributor.slice()); - flashListChunks = chunkByProperties( - flashes.map((flash) => ({ - act: flash.act, - flash, - date: flash.date, - // Manual artists/contrib properties here, 8ecause we don't - // want to show the full list of other contri8utors inline. - // (It can often 8e very, very large!) - artists: [], - contrib: flash.contributorContribs.find(({who}) => who === artist), - })), - ['act'] - ).map(({act, chunk}) => ({ - act, - chunk, - dateFirst: chunk[0].date, - dateLast: chunk[chunk.length - 1].date, - })); - } - - const generateEntryAccents = ({ - getArtistString, - language, - original, - entry, - artists, - contrib, - }) => - original - ? language.$('artistPage.creditList.entry.rerelease', {entry}) - : !empty(artists) - ? contrib.what || contrib.whatArray?.length - ? language.$('artistPage.creditList.entry.withArtists.withContribution', { - entry, - artists: getArtistString(artists), - contribution: contrib.whatArray - ? language.formatUnitList(contrib.whatArray) - : contrib.what, - }) - : language.$('artistPage.creditList.entry.withArtists', { - entry, - artists: getArtistString(artists), - }) - : contrib.what || contrib.whatArray?.length - ? language.$('artistPage.creditList.entry.withContribution', { - entry, - contribution: contrib.whatArray - ? language.formatUnitList(contrib.whatArray) - : contrib.what, - }) - : entry; - - const unbound_generateTrackList = (chunks, { - getArtistString, - html, - language, - link, - }) => - html.tag('dl', - chunks.flatMap(({date, album, chunk, duration}) => [ - html.tag('dt', - date && duration ? - language.$('artistPage.creditList.album.withDate.withDuration', { - album: link.album(album), - date: language.formatDate(date), - duration: language.formatDuration(duration, { - approximate: true, - }), - }) : - - date ? - language.$('artistPage.creditList.album.withDate', { - album: link.album(album), - date: language.formatDate(date), - }) : - - duration ? - language.$('artistPage.creditList.album.withDuration', { - album: link.album(album), - duration: language.formatDuration(duration, { - approximate: true, - }), - }) : - - language.$('artistPage.creditList.album', { - album: link.album(album), - })), - - html.tag('dd', - html.tag('ul', - chunk - .map(({track, ...props}) => ({ - original: track.originalReleaseTrack, - entry: language.$('artistPage.creditList.entry.track.withDuration', { - track: link.track(track), - duration: language.formatDuration(track.duration ?? 0), - }), - ...props, - })) - .map(({original, ...opts}) => - html.tag('li', - {class: original && 'rerelease'}, - generateEntryAccents({ - getArtistString, - language, - original, - ...opts, - }) - ) - ))), - ])); - - const unbound_serializeArtistsAndContrib = - (key, {serializeContribs, serializeLink}) => - (thing) => { - const {artists, contrib} = getArtistsAndContrib(thing, key); - const ret = {}; - ret.link = serializeLink(thing); - if (contrib.what) ret.contribution = contrib.what; - if (!empty(artists)) ret.otherArtists = serializeContribs(artists); - return ret; - }; - - const unbound_serializeTrackListChunks = (chunks, {serializeLink}) => - chunks.map(({date, album, chunk, duration}) => ({ - album: serializeLink(album), - date, - duration, - tracks: chunk.map(({track}) => ({ - link: serializeLink(track), - duration: track.duration, - })), - })); - - const jumpTo = { - tracks: !empty(allTracks), - art: !empty(artThingsAll), - flashes: wikiInfo.enableFlashesAndGames && !empty(flashes), - commentary: !empty(commentaryThings), - }; - - const showJumpTo = Object.values(jumpTo).includes(true); - - const data = { - type: 'data', - path: ['artist', artist.directory], - data: ({serializeContribs, serializeLink}) => { - const serializeArtistsAndContrib = bindOpts(unbound_serializeArtistsAndContrib, { - serializeContribs, - serializeLink, - }); - - const serializeTrackListChunks = bindOpts(unbound_serializeTrackListChunks, { - serializeLink, - }); - - return { - albums: { - asCoverArtist: artist.albumsAsCoverArtist - .map(serializeArtistsAndContrib('coverArtistContribs')), - asWallpaperArtist: artist.albumsAsWallpaperArtist - .map(serializeArtistsAndContrib('wallpaperArtistContribs')), - asBannerArtist: artist.albumsAsBannerArtis - .map(serializeArtistsAndContrib('bannerArtistContribs')), - }, - flashes: wikiInfo.enableFlashesAndGames - ? { - asContributor: artist.flashesAsContributor - .map(flash => getArtistsAndContrib(flash, 'contributorContribs')) - .map(({contrib, thing: flash}) => ({ - link: serializeLink(flash), - contribution: contrib.what, - })), - } - : null, - tracks: { - asArtist: artist.tracksAsArtist - .map(serializeArtistsAndContrib('artistContribs')), - asContributor: artist.tracksAsContributo - .map(serializeArtistsAndContrib('contributorContribs')), - chunked: serializeTrackListChunks(trackListChunks), - }, - }; - }, - }; - - const infoPage = { - type: 'page', - path: ['artist', artist.directory], - page: ({ - fancifyURL, - generateInfoGalleryLinks, - getArtistAvatar, - getArtistString, - html, - link, - language, - transformMultiline, - }) => { - const generateTrackList = bindOpts(unbound_generateTrackList, { - getArtistString, - html, - language, - link, - }); - - return { - title: language.$('artistPage.title', {artist: name}), - - cover: artist.hasAvatar && { - src: getArtistAvatar(artist), - alt: language.$('misc.alt.artistAvatar'), - }, - - main: { - headingMode: 'sticky', - - content: [ - ...html.fragment( - contextNotes && [ - html.tag('p', - language.$('releaseInfo.note')), - - html.tag('blockquote', - transformMultiline(contextNotes)), - - html.tag('hr'), - ]), - - !empty(urls) && - html.tag('p', - language.$('releaseInfo.visitOn', { - links: language.formatDisjunctionList( - urls.map((url) => fancifyURL(url, {language})) - ), - })), - - hasGallery && - html.tag('p', - language.$('artistPage.viewArtGallery', { - link: link.artistGallery(artist, { - text: language.$('artistPage.viewArtGallery.link'), - }), - })), - - showJumpTo && - html.tag('p', - language.$('misc.jumpTo.withLinks', { - links: language.formatUnitList( - [ - jumpTo.tracks && - html.tag('a', - {href: '#tracks'}, - language.$('artistPage.trackList.title')), - - jumpTo.art && - html.tag('a', - {href: '#art'}, - language.$('artistPage.artList.title')), - - jumpTo.flashes && - html.tag('a', - {href: '#flashes'}, - language.$('artistPage.flashList.title')), - - jumpTo.commentary && - html.tag('a', - {href: '#commentary'}, - language.$('artistPage.commentaryList.title')), - ].filter(Boolean)), - })), - - ...html.fragment( - !empty(allTracks) && [ - html.tag('h2', - {id: 'tracks', class: ['content-heading']}, - language.$('artistPage.trackList.title')), - - totalDuration > 0 && - html.tag('p', - language.$('artistPage.contributedDurationLine', { - artist: artist.name, - duration: language.formatDuration( - totalDuration, - { - approximate: true, - unit: true, - } - ), - })), - - !empty(musicGroups) && - html.tag('p', - language.$('artistPage.musicGroupsLine', { - groups: language.formatUnitList( - musicGroups.map(({group, contributions}) => - language.$('artistPage.groupsLine.item', { - group: link.groupInfo(group), - contributions: - language.countContributions( - contributions - ), - }) - ) - ), - })), - - generateTrackList(trackListChunks), - ]), - - ...html.fragment( - !empty(artThingsAll) && [ - html.tag('h2', - {id: 'art', class: ['content-heading']}, - language.$('artistPage.artList.title')), - - hasGallery && - html.tag('p', - language.$('artistPage.viewArtGallery.orBrowseList', { - link: link.artistGallery(artist, { - text: language.$('artistPage.viewArtGallery.link'), - }) - })), - - !empty(artGroups) && - html.tag('p', - language.$('artistPage.artGroupsLine', { - groups: language.formatUnitList( - artGroups.map(({group, contributions}) => - language.$('artistPage.groupsLine.item', { - group: link.groupInfo(group), - contributions: - language.countContributions( - contributions - ), - }) - ) - ), - })), - - html.tag('dl', - artListChunks.flatMap(({date, album, chunk}) => [ - html.tag('dt', language.$('artistPage.creditList.album.withDate', { - album: link.album(album), - date: language.formatDate(date), - })), - - html.tag('dd', - html.tag('ul', - chunk - .map(({track, key, ...props}) => ({ - ...props, - entry: - track - ? language.$('artistPage.creditList.entry.track', { - track: link.track(track), - }) - : html.tag('i', - language.$('artistPage.creditList.entry.album.' + { - wallpaperArtistContribs: - 'wallpaperArt', - bannerArtistContribs: - 'bannerArt', - coverArtistContribs: - 'coverArt', - }[key])), - })) - .map((opts) => generateEntryAccents({ - getArtistString, - language, - ...opts, - })) - .map(row => html.tag('li', row)))), - ])), - ]), - - ...html.fragment( - wikiInfo.enableFlashesAndGames && - !empty(flashes) && [ - html.tag('h2', - {id: 'flashes', class: ['content-heading']}, - language.$('artistPage.flashList.title')), - - html.tag('dl', - flashListChunks.flatMap(({ - act, - chunk, - dateFirst, - dateLast, - }) => [ - html.tag('dt', - language.$('artistPage.creditList.flashAct.withDateRange', { - act: link.flash(chunk[0].flash, { - text: act.name, - }), - dateRange: language.formatDateRange( - dateFirst, - dateLast - ), - })), - - html.tag('dd', - html.tag('ul', - chunk - .map(({flash, ...props}) => ({ - ...props, - entry: language.$('artistPage.creditList.entry.flash', { - flash: link.flash(flash), - }), - })) - .map(opts => generateEntryAccents({ - getArtistString, - language, - ...opts, - })) - .map(row => html.tag('li', row)))), - ])), - ]), - - ...html.fragment( - !empty(commentaryThings) && [ - html.tag('h2', - {id: 'commentary', class: ['content-heading']}, - language.$('artistPage.commentaryList.title')), - - html.tag('dl', - commentaryListChunks.flatMap(({album, chunk}) => [ - html.tag('dt', - language.$('artistPage.creditList.album', { - album: link.album(album), - })), - - html.tag('dd', - html.tag('ul', - chunk - .map(({track}) => track - ? language.$('artistPage.creditList.entry.track', { - track: link.track(track), - }) - : html.tag('i', - language.$('artistPage.creditList.entry.album.commentary'))) - .map(row => html.tag('li', row)))), - ])), - ]), - ], - }, - - nav: generateNavForArtist(artist, false, hasGallery, { - generateInfoGalleryLinks, - link, - language, - wikiData, - }), - }; - }, - }; - - const galleryPage = hasGallery && { - type: 'page', - path: ['artistGallery', artist.directory], - page: ({ - generateInfoGalleryLinks, - getAlbumCover, - getGridHTML, - getTrackCover, - html, - link, - language, - }) => ({ - title: language.$('artistGalleryPage.title', {artist: name}), - - main: { - classes: ['top-index'], - headingMode: 'static', - - content: [ - html.tag('p', - {class: 'quick-info'}, - language.$('artistGalleryPage.infoLine', { - coverArts: language.countCoverArts(artThingsGallery.length, { - unit: true, - }), - })), - - html.tag('div', - {class: 'grid-listing'}, - getGridHTML({ - entries: artThingsGallery.map((item) => ({item})), - srcFn: (thing) => - thing.album - ? getTrackCover(thing) - : getAlbumCover(thing), - linkFn: (thing, opts) => - thing.album - ? link.track(thing, opts) - : link.album(thing, opts), - })), - ], - }, - - nav: generateNavForArtist(artist, true, hasGallery, { - generateInfoGalleryLinks, - link, - language, - wikiData, - }), - }), - }; - - return [data, infoPage, galleryPage].filter(Boolean); -} - -// Utility functions - -function generateNavForArtist(artist, isGallery, hasGallery, { - generateInfoGalleryLinks, - language, - link, - wikiData, -}) { - const {wikiInfo} = wikiData; +export function pathsForTarget(artist) { + return [ + { + type: 'page', + path: ['artist', artist.directory], - const infoGalleryLinks = - hasGallery && - generateInfoGalleryLinks(artist, isGallery, { - link, - language, - linkKeyGallery: 'artistGallery', - linkKeyInfo: 'artist', - }); - - return { - linkContainerClasses: ['nav-links-hierarchy'], - links: [ - {toHome: true}, - wikiInfo.enableListings && { - path: ['localized.listingIndex'], - title: language.$('listingIndex.title'), - }, - { - html: language.$('artistPage.nav.artist', { - artist: link.artist(artist, {class: 'current'}), - }), + contentFunction: { + name: 'generateArtistInfoPage', + args: [artist], }, - hasGallery && { - divider: false, - html: `(${infoGalleryLinks})`, - }, - ], - }; + }, + ]; } diff --git a/src/page/index.js b/src/page/index.js index f580cbea..77ebfb6f 100644 --- a/src/page/index.js +++ b/src/page/index.js @@ -2,52 +2,20 @@ // other modules here! It's not the page spec for the homepage - see // homepage.js for that. // -// Each module published in this list should follow a particular format, -// including any of the following exports: +// (TODO: The docs here from initial draft were totally outdated. +// We don't have docs for the new setup yet. +// Write those!!) // -// condition({wikiData}) -// Returns a boolean indicating whether to process targets/writes (true) or -// skip this page spec altogether (false). This is usually used for -// selectively toggling pages according to site feature flags, though it may -// also be used to e.g. skip out if no targets would be found (preventing -// writeTargetless from generating an empty index page). -// -// targets({wikiData}) -// Gets the objects which this page's write() function should be called on. -// Usually this will simply mean returning the appropriate thingData array, -// but it may also apply filter/map/etc if useful. -// -// write(thing, {wikiData}) -// Provides descriptors for any page and data writes associated with the -// given thing (which will be a value from the targets() array). This -// includes page (HTML) writes, data (JSON) writes, etc. Notably, this -// function does not perform any file operations itself; it only describes -// the operations which will be processed elsewhere, once for each -// translation language. The write function also immediately transforms -// any data which will be reused across writes of the same page, so that -// this data is effectively cached (rather than recalculated for each -// language/write). -// -// writeTargetless({wikiData}) -// Provides descriptors for page/data/etc writes which will be used -// without concern for targets. This is usually used for writing index pages -// which should be generated just once (rather than corresponding to -// targets). -// -// As these modules are effectively the HTML templates for all site layout, -// common patterns may also be exported alongside the special exports above. -// These functions should be referenced only from adjacent modules, as they -// pertain only to site page generation. export * as album from './album.js'; -export * as albumCommentary from './album-commentary.js'; +// export * as albumCommentary from './album-commentary.js'; export * as artist from './artist.js'; -export * as artistAlias from './artist-alias.js'; -export * as flash from './flash.js'; -export * as group from './group.js'; -export * as homepage from './homepage.js'; -export * as listing from './listing.js'; -export * as news from './news.js'; +// export * as artistAlias from './artist-alias.js'; +// export * as flash from './flash.js'; +// export * as group from './group.js'; +// export * as homepage from './homepage.js'; +// export * as listing from './listing.js'; +// export * as news from './news.js'; export * as static from './static.js'; -export * as tag from './tag.js'; +// export * as tag from './tag.js'; export * as track from './track.js'; diff --git a/src/page/static.js b/src/page/static.js index 8572db4e..82330dec 100644 --- a/src/page/static.js +++ b/src/page/static.js @@ -8,26 +8,16 @@ export function targets({wikiData}) { return wikiData.staticPageData; } -export function write(staticPage) { - const page = { - type: 'page', - path: ['staticPage', staticPage.directory], - page: ({ - transformMultiline, - }) => ({ - title: staticPage.name, - stylesheet: staticPage.stylesheet, +export function pathsForTarget(staticPage) { + return [ + { + type: 'page', + path: ['staticPage', staticPage.directory], - main: { - classes: ['long-content'], - headingMode: 'sticky', - - content: transformMultiline(staticPage.content), + contentFunction: { + name: 'generateStaticPage', + args: [staticPage], }, - - nav: {simple: true}, - }), - }; - - return [page]; + }, + ]; } diff --git a/src/page/track.js b/src/page/track.js index b6b03f35..e75b6958 100644 --- a/src/page/track.js +++ b/src/page/track.js @@ -1,553 +1,21 @@ // Track page specification. -import { - generateAlbumChronologyLinks, - generateAlbumNavLinks, - generateAlbumSecondaryNav, - generateAlbumSidebar, - generateAlbumAdditionalFilesList as unbound_generateAlbumAdditionalFilesList, -} from './album.js'; - -import { - bindOpts, - empty, -} from '../util/sugar.js'; - -import { - getTrackCover, - getAlbumListTag, - sortFlashesChronologically, -} from '../util/wiki-data.js'; - export const description = `per-track info pages`; export function targets({wikiData}) { return wikiData.trackData; } -export function write(track, {wikiData}) { - const {wikiInfo} = wikiData; - - const { - album, - contributorContribs, - referencedByTracks, - referencedTracks, - sampledByTracks, - sampledTracks, - otherReleases, - } = track; - - const listTag = getAlbumListTag(album); - - let flashesThatFeature; - if (wikiInfo.enableFlashesAndGames) { - flashesThatFeature = sortFlashesChronologically( - [track, ...otherReleases].flatMap((track) => - track.featuredInFlashes.map((flash) => ({ - flash, - as: track, - directory: flash.directory, - name: flash.name, - date: flash.date, - })) - ) - ); - } - - const unbound_getTrackItem = (track, { - getArtistString, - html, - language, - link, - }) => - html.tag('li', - language.$('trackList.item.withArtists', { - track: link.track(track), - by: html.tag('span', - {class: 'by'}, - language.$('trackList.item.withArtists.by', { - artists: getArtistString(track.artistContribs), - })), - })); - - const hasCommentary = - track.commentary || otherReleases.some((t) => t.commentary); +export function pathsForTarget(track) { + return [ + { + type: 'page', + path: ['track', track.directory], - const hasAdditionalFiles = !empty(track.additionalFiles); - const hasSheetMusicFiles = !empty(track.sheetMusicFiles); - const hasMidiProjectFiles = !empty(track.midiProjectFiles); - const numAdditionalFiles = album.additionalFiles.flatMap((g) => g.files).length; - - const generateCommentary = ({language, link, transformMultiline}) => - transformMultiline([ - track.commentary, - ...otherReleases.map((track) => - track.commentary - ?.split('\n') - .filter((line) => line.replace(/<\/b>/g, '').includes(':</i>')) - .flatMap(line => [ - line, - language.$('releaseInfo.artistCommentary.seeOriginalRelease', { - original: link.track(track), - }), - ]) - .join('\n') - ), - ].filter(Boolean).join('\n')); - - const data = { - type: 'data', - path: ['track', track.directory], - data: ({ - serializeContribs, - serializeCover, - serializeGroupsForTrack, - serializeLink, - }) => ({ - name: track.name, - directory: track.directory, - dates: { - released: track.date, - originallyReleased: track.originalDate, - coverArtAdded: track.coverArtDate, + contentFunction: { + name: 'generateTrackInfoPage', + args: [track], }, - duration: track.duration, - color: track.color, - cover: serializeCover(track, getTrackCover), - artistsContribs: serializeContribs(track.artistContribs), - contributorContribs: serializeContribs(track.contributorContribs), - coverArtistContribs: serializeContribs(track.coverArtistContribs || []), - album: serializeLink(track.album), - groups: serializeGroupsForTrack(track), - references: track.references.map(serializeLink), - referencedBy: track.referencedBy.map(serializeLink), - alsoReleasedAs: otherReleases.map((track) => ({ - track: serializeLink(track), - album: serializeLink(track.album), - })), - }), - }; - - const getSocialEmbedDescription = ({ - getArtistString: _getArtistString, - language, - }) => { - const hasArtists = !empty(track.artistContribs); - const hasCoverArtists = !empty(track.coverArtistContribs); - const getArtistString = (contribs) => - _getArtistString(contribs, { - // We don't want to put actual HTML tags in social embeds (sadly - // they don't get parsed and displayed, generally speaking), so - // override the link argument so that artist "links" just show - // their names. - link: {artist: (artist) => artist.name}, - }); - if (!hasArtists && !hasCoverArtists) return ''; - return language.formatString( - 'trackPage.socialEmbed.body' + - [hasArtists && '.withArtists', hasCoverArtists && '.withCoverArtists'] - .filter(Boolean) - .join(''), - Object.fromEntries( - [ - hasArtists && ['artists', getArtistString(track.artistContribs)], - hasCoverArtists && [ - 'coverArtists', - getArtistString(track.coverArtistContribs), - ], - ].filter(Boolean) - ) - ); - }; - - const page = { - type: 'page', - path: ['track', track.directory], - page: ({ - absoluteTo, - fancifyURL, - generateAdditionalFilesList, - generateAdditionalFilesShortcut, - generateChronologyLinks, - generateContentHeading, - generateNavigationLinks, - generateTrackListDividedByGroups, - getAlbumStylesheet, - getArtistString, - getLinkThemeString, - getSizeOfAdditionalFile, - getThemeString, - getTrackCover, - html, - link, - language, - transformLyrics, - transformMultiline, - to, - urls, - }) => { - const getTrackItem = bindOpts(unbound_getTrackItem, { - getArtistString, - html, - language, - link, - }); - - const generateAlbumAdditionalFilesList = bindOpts(unbound_generateAlbumAdditionalFilesList, { - [bindOpts.bindIndex]: 2, - generateAdditionalFilesList, - getSizeOfAdditionalFile, - link, - urls, - }); - - return { - title: language.$('trackPage.title', {track: track.name}), - stylesheet: getAlbumStylesheet(album, {to}), - - themeColor: track.color, - theme: - getThemeString(track.color, { - additionalVariables: [ - `--album-directory: ${album.directory}`, - `--track-directory: ${track.directory}`, - ] - }), - - socialEmbed: { - heading: language.$('trackPage.socialEmbed.heading', { - album: track.album.name, - }), - headingLink: absoluteTo('localized.album', album.directory), - title: language.$('trackPage.socialEmbed.title', { - track: track.name, - }), - description: getSocialEmbedDescription({getArtistString, language}), - image: '/' + getTrackCover(track, {to: urls.from('shared.root').to}), - color: track.color, - }, - - // disabled for now! shifting banner position per height of page is disorienting - /* - banner: !empty(album.bannerArtistContribs) && { - classes: ['dim'], - dimensions: album.bannerDimensions, - path: ['media.albumBanner', album.directory, album.bannerFileExtension], - alt: language.$('misc.alt.albumBanner'), - position: 'bottom' - }, - */ - - cover: { - src: getTrackCover(track), - alt: language.$('misc.alt.trackCover'), - artTags: track.artTags, - }, - - main: { - headingMode: 'sticky', - - content: [ - html.tag('p', - { - [html.onlyIfContent]: true, - [html.joinChildren]: '<br>', - }, - [ - !empty(track.artistContribs) && - language.$('releaseInfo.by', { - artists: getArtistString(track.artistContribs, { - showContrib: true, - showIcons: true, - }), - }), - - !empty(track.coverArtistContribs) && - language.$('releaseInfo.coverArtBy', { - artists: getArtistString(track.coverArtistContribs, { - showContrib: true, - showIcons: true, - }), - }), - - track.date && - language.$('releaseInfo.released', { - date: language.formatDate(track.date), - }), - - track.hasCoverArt && - track.coverArtDate && - +track.coverArtDate !== +track.date && - language.$('releaseInfo.artReleased', { - date: language.formatDate(track.coverArtDate), - }), - - track.duration && - language.$('releaseInfo.duration', { - duration: language.formatDuration( - track.duration - ), - }), - ]), - - html.tag('p', - { - [html.onlyIfContent]: true, - [html.joinChildren]: '<br>', - }, - [ - hasSheetMusicFiles && - language.$('releaseInfo.sheetMusicFiles.shortcut', { - link: html.tag('a', - {href: '#sheet-music-files'}, - language.$('releaseInfo.sheetMusicFiles.shortcut.link')), - }), - - hasMidiProjectFiles && - language.$('releaseInfo.midiProjectFiles.shortcut', { - link: html.tag('a', - {href: '#midi-project-files'}, - language.$('releaseInfo.midiProjectFiles.shortcut.link')), - }), - - hasAdditionalFiles && - generateAdditionalFilesShortcut(track.additionalFiles), - ]), - - html.tag('p', - (empty(track.urls) - ? language.$('releaseInfo.listenOn.noLinks') - : language.$('releaseInfo.listenOn', { - links: language.formatDisjunctionList( - track.urls.map(url => fancifyURL(url, {language}))), - }))), - - ...html.fragment( - !empty(otherReleases) && [ - generateContentHeading({ - id: 'also-released-as', - title: language.$('releaseInfo.alsoReleasedAs'), - }), - - html.tag('ul', otherReleases.map(track => - html.tag('li', language.$('releaseInfo.alsoReleasedAs.item', { - track: link.track(track), - album: link.album(track.album), - })))), - ]), - - ...html.fragment( - !empty(contributorContribs) && [ - generateContentHeading({ - id: 'contributors', - title: language.$('releaseInfo.contributors'), - }), - - html.tag('ul', contributorContribs.map(contrib => - html.tag('li', getArtistString([contrib], { - showContrib: true, - showIcons: true, - })))), - ]), - - ...html.fragment( - !empty(referencedTracks) && [ - generateContentHeading({ - id: 'references', - title: - language.$('releaseInfo.tracksReferenced', { - track: html.tag('i', track.name), - }), - }), - - html.tag('ul', referencedTracks.map(getTrackItem)), - ]), - - ...html.fragment( - !empty(referencedByTracks) && [ - generateContentHeading({ - id: 'referenced-by', - title: - language.$('releaseInfo.tracksThatReference', { - track: html.tag('i', track.name), - }), - }), - - generateTrackListDividedByGroups(referencedByTracks, { - getTrackItem, - wikiData, - }), - ]), - - ...html.fragment( - !empty(sampledTracks) && [ - generateContentHeading({ - id: 'samples', - title: - language.$('releaseInfo.tracksSampled', { - track: html.tag('i', track.name), - }), - }), - - html.tag('ul', sampledTracks.map(getTrackItem)), - ]), - - ...html.fragment( - !empty(sampledByTracks) && [ - generateContentHeading({ - id: 'sampled-by', - title: - language.$('releaseInfo.tracksThatSample', { - track: html.tag('i', track.name), - }) - }), - - html.tag('ul', sampledByTracks.map(getTrackItem)), - ]), - - ...html.fragment( - wikiInfo.enableFlashesAndGames && - !empty(flashesThatFeature) && [ - generateContentHeading({ - id: 'featured-in', - title: - language.$('releaseInfo.flashesThatFeature', { - track: html.tag('i', track.name), - }), - }), - - html.tag('ul', flashesThatFeature.map(({flash, as}) => - html.tag('li', - {class: as !== track && 'rerelease'}, - (as === track - ? language.$('releaseInfo.flashesThatFeature.item', { - flash: link.flash(flash), - }) - : language.$('releaseInfo.flashesThatFeature.item.asDifferentRelease', { - flash: link.flash(flash), - track: link.track(as), - }))))), - ]), - - ...html.fragment( - track.lyrics && [ - generateContentHeading({ - id: 'lyrics', - title: language.$('releaseInfo.lyrics'), - }), - - html.tag('blockquote', transformLyrics(track.lyrics)), - ]), - - ...html.fragment( - hasSheetMusicFiles && [ - generateContentHeading({ - id: 'sheet-music-files', - title: language.$('releaseInfo.sheetMusicFiles.heading'), - }), - - generateAlbumAdditionalFilesList(album, track.sheetMusicFiles, { - fileSize: false, - }), - ]), - - ...html.fragment( - hasMidiProjectFiles && [ - generateContentHeading({ - id: 'midi-project-files', - title: language.$('releaseInfo.midiProjectFiles.heading'), - }), - - generateAlbumAdditionalFilesList(album, track.midiProjectFiles), - ]), - - ...html.fragment( - hasAdditionalFiles && [ - generateContentHeading({ - id: 'additional-files', - title: language.$('releaseInfo.additionalFiles.heading', { - additionalFiles: language.countAdditionalFiles(numAdditionalFiles, { - unit: true, - }), - }) - }), - - generateAlbumAdditionalFilesList(album, track.additionalFiles), - ]), - - ...html.fragment( - hasCommentary && [ - generateContentHeading({ - id: 'artist-commentary', - title: language.$('releaseInfo.artistCommentary'), - }), - - html.tag('blockquote', generateCommentary({ - link, - language, - transformMultiline, - })), - ]), - ], - }, - - sidebarLeft: generateAlbumSidebar(album, track, { - fancifyURL, - getLinkThemeString, - html, - language, - link, - transformMultiline, - wikiData, - }), - - nav: { - linkContainerClasses: ['nav-links-hierarchy'], - links: [ - {toHome: true}, - { - path: ['localized.album', album.directory], - title: album.name, - }, - listTag === 'ol' && - { - html: language.$('trackPage.nav.track.withNumber', { - number: album.tracks.indexOf(track) + 1, - track: link.track(track, {class: 'current', to}), - }), - }, - listTag === 'ul' && - { - html: language.$('trackPage.nav.track', { - track: link.track(track, {class: 'current', to}), - }), - }, - ].filter(Boolean), - - content: generateAlbumChronologyLinks(album, track, { - generateChronologyLinks, - html, - }), - - bottomRowContent: - album.tracks.length > 1 && - generateAlbumNavLinks(album, track, { - generateNavigationLinks, - html, - language, - }), - }, - - secondaryNav: generateAlbumSecondaryNav(album, track, { - getLinkThemeString, - html, - language, - link, - }), - }; }, - }; - - return [data, page]; + ]; } diff --git a/src/static/client.js b/src/static/client.js index efae8501..35ef82ee 100644 --- a/src/static/client.js +++ b/src/static/client.js @@ -216,6 +216,7 @@ fetch(rebase('data.json', 'rebaseShared')) // Data & info card --------------------------------------- +/* const NORMAL_HOVER_INFO_DELAY = 750; const FAST_HOVER_INFO_DELAY = 250; const END_FAST_HOVER_DELAY = 500; @@ -444,6 +445,7 @@ function addInfoCardLinkHandlers(type) { if (localStorage.tryInfoCards) { addInfoCardLinkHandlers('track'); } +*/ // Custom hash links -------------------------------------- diff --git a/src/static/site3.css b/src/static/site4.css index 3ebe782d..28a4924d 100644 --- a/src/static/site3.css +++ b/src/static/site4.css @@ -433,11 +433,15 @@ a:hover { text-decoration: underline; } -.nav-main-links > span { +a.current { + font-weight: 800; +} + +.nav-main-links > span > span { white-space: nowrap; } -.nav-main-links > span > a.current { +.nav-main-links > span.current > span.nav-link-content > a { font-weight: 800; } @@ -447,7 +451,7 @@ a:hover { font-weight: 800; } -.nav-links-hierarchy > span:not(:first-child):not(.no-divider)::before { +.nav-links-hierarchical > span:not(:first-child):not(.no-divider)::before { content: "\0020/\0020"; } @@ -1358,6 +1362,24 @@ main.long-content .content-sticky-heading-container .content-sticky-subheading-r contain: paint; } +/* Sticky sidebar */ + +.sidebar-column.sidebar.sticky-column, +.sidebar-column.sidebar.sticky-last, +.sidebar-multiple.sticky-last > .sidebar:last-child, +.sidebar-multiple.sticky-column { + position: sticky; + top: 10px; +} + +.sidebar-multiple.sticky-last { + align-self: stretch; +} + +.sidebar-multiple.sticky-column { + align-self: flex-start; +} + /* Image overlay */ #image-overlay-container { diff --git a/src/strings-default.json b/src/strings-default.json index a075f445..345f20ff 100644 --- a/src/strings-default.json +++ b/src/strings-default.json @@ -96,14 +96,19 @@ "releaseInfo.viewCommentary.link": "commentary page", "releaseInfo.viewGallery": "View {LINK}!", "releaseInfo.viewGallery.link": "gallery page", + "releaseInfo.viewGalleryOrCommentary": "View {GALLERY} or {COMMENTARY}!", + "releaseInfo.viewGalleryOrCommentary.gallery": "gallery page", + "releaseInfo.viewGalleryOrCommentary.commentary": "commentary page", "releaseInfo.viewOriginalFile": "View {LINK}.", "releaseInfo.viewOriginalFile.withSize": "View {LINK} ({SIZE}).", "releaseInfo.viewOriginalFile.link": "original file", "releaseInfo.viewOriginalFile.sizeWarning": "(Heads up! If you're on a mobile plan, this is a large download.)", "releaseInfo.listenOn": "Listen on {LINKS}.", - "releaseInfo.listenOn.noLinks": "This track has no URLs at which it can be listened.", + "releaseInfo.listenOn.noLinks": "This wiki doesn't have any listening links for {NAME}.", "releaseInfo.visitOn": "Visit on {LINKS}.", "releaseInfo.playOn": "Play on {LINKS}.", + "releaseInfo.readCommentary": "Read {LINK}.", + "releaseInfo.readCommentary.link": "artist commentary", "releaseInfo.alsoReleasedAs": "Also released as:", "releaseInfo.alsoReleasedAs.item": "{TRACK} (on {ALBUM})", "releaseInfo.contributors": "Contributors:", @@ -120,8 +125,8 @@ "releaseInfo.artistCommentary.seeOriginalRelease": "See {ORIGINAL}!", "releaseInfo.artTags": "Tags:", "releaseInfo.artTags.inline": "Tags: {TAGS}", - "releaseInfo.additionalFiles.shortcut": "{ANCHOR_LINK} {TITLES}", - "releaseInfo.additionalFiles.shortcut.anchorLink": "Additional files:", + "releaseInfo.additionalFiles.shortcut": "View {ANCHOR_LINK}: {TITLES}", + "releaseInfo.additionalFiles.shortcut.anchorLink": "additional files", "releaseInfo.additionalFiles.heading": "View or download {ADDITIONAL_FILES}:", "releaseInfo.additionalFiles.entry": "{TITLE}", "releaseInfo.additionalFiles.entry.withDescription": "{TITLE}: {DESCRIPTION}", @@ -133,10 +138,10 @@ "releaseInfo.midiProjectFiles.shortcut": "Download {LINK}.", "releaseInfo.midiProjectFiles.shortcut.link": "MIDI/project files", "releaseInfo.midiProjectFiles.heading": "Download MIDI/project files:", - "releaseInfo.note": "Note:", + "releaseInfo.note": "Context notes:", "trackList.section.withDuration": "{SECTION} ({DURATION}):", - "trackList.group": "{GROUP}:", - "trackList.group.other": "Other", + "trackList.group": "From {GROUP}:", + "trackList.group.fromOther": "From somewhere else:", "trackList.item.withDuration": "({DURATION}) {TRACK}", "trackList.item.withDuration.withArtists": "({DURATION}) {TRACK} {BY}", "trackList.item.withArtists": "{TRACK} {BY}", @@ -270,7 +275,8 @@ "artistPage.contributedDurationLine": "{ARTIST} has contributed {DURATION} of music shared on this wiki.", "artistPage.musicGroupsLine": "Contributed music to groups: {GROUPS}", "artistPage.artGroupsLine": "Contributed art to groups: {GROUPS}", - "artistPage.groupsLine.item": "{GROUP} ({CONTRIBUTIONS})", + "artistPage.groupsLine.item.withCount": "{GROUP} ({COUNT})", + "artistPage.groupsLine.item.withDuration": "{GROUP} ({DURATION})", "artistPage.trackList.title": "Tracks", "artistPage.artList.title": "Artworks", "artistPage.flashList.title": "Flashes & Games", diff --git a/src/upd8.js b/src/upd8.js index 9f54b3bb..366dc21b 100755 --- a/src/upd8.js +++ b/src/upd8.js @@ -772,6 +772,7 @@ async function main() { developersComment, getSizeOfAdditionalFile, getSizeOfImageFile, + niceShowAggregate, }); } diff --git a/src/util/html.js b/src/util/html.js index 1c55fb8c..b5930d06 100644 --- a/src/util/html.js +++ b/src/util/html.js @@ -1,5 +1,8 @@ // Some really simple functions for formatting HTML content. +import * as commonValidators from '../data/things/validators.js'; +import {empty} from './sugar.js'; + // COMPREHENSIVE! // https://html.spec.whatwg.org/multipage/syntax.html#void-elements export const selfClosingTags = [ @@ -38,128 +41,731 @@ export const joinChildren = Symbol(); // or when there are multiple children. export const noEdgeWhitespace = Symbol(); -export function tag(tagName, ...args) { - const selfClosing = selfClosingTags.includes(tagName); +// Note: This is only guaranteed to return true for blanks (as returned by +// html.blank()) and false for Tags and Templates (regardless of contents or +// other properties). Don't depend on this to match any other values. +export function isBlank(value) { + if (isTag(value)) { + return false; + } + + if (isTemplate(value)) { + return false; + } + + if (!Array.isArray(value)) { + return false; + } + + return value.length === 0; +} + +export function isTag(value) { + return value instanceof Tag; +} + +export function isTemplate(value) { + return value instanceof Template; +} + +export function isHTML(value) { + if (typeof value === 'string') { + return true; + } + + if (value === null || value === undefined || value === false) { + return true; + } + + if (isBlank(value) || isTag(value) || isTemplate(value)) { + return true; + } + + if (Array.isArray(value)) { + if (value.every(isHTML)) { + return true; + } + } + + return false; +} + +export function isAttributes(value) { + if (typeof value !== 'object' || Array.isArray(value)) { + return false; + } + + if (value === null) { + return false; + } + + if (isTag(value) || isTemplate(value)) { + return false; + } + + // TODO: Validate attribute values (just the general shape) - let openTag; + return true; +} + +export const validators = { + // TODO: Move above implementations here and detail errors + + isBlank(value) { + if (!isBlank(value)) { + throw new TypeError(`Expected html.blank()`); + } + + return true; + }, + + isTag(value) { + if (!isTag(value)) { + throw new TypeError(`Expected HTML tag`); + } + + return true; + }, + + isTemplate(value) { + if (!isTemplate(value)) { + throw new TypeError(`Expected HTML template`); + } + + return true; + }, + + isHTML(value) { + if (!isHTML(value)) { + throw new TypeError(`Expected HTML content`); + } + + return true; + }, + + isAttributes(value) { + if (!isAttributes(value)) { + throw new TypeError(`Expected HTML attributes`); + } + + return true; + }, +}; + +export function blank() { + return []; +} + +export function tag(tagName, ...args) { let content; - let attrs; + let attributes; - if (typeof args[0] === 'object' && !Array.isArray(args[0])) { - attrs = args[0]; + if ( + typeof args[0] === 'object' && + !(Array.isArray(args[0]) || + args[0] instanceof Tag || + args[0] instanceof Template) + ) { + attributes = args[0]; content = args[1]; } else { content = args[0]; } - if (selfClosing && content) { - throw new Error(`Tag <${tagName}> is self-closing but got content!`); + return new Tag(tagName, attributes, content); +} + +export function tags(content) { + return new Tag(null, null, content); +} + +export class Tag { + #tagName = ''; + #content = null; + #attributes = null; + + constructor(tagName, attributes, content) { + this.tagName = tagName; + this.attributes = attributes; + this.content = content; + } + + clone() { + return new Tag(this.tagName, this.attributes, this.content); } - if (Array.isArray(content)) { - if (content.some(item => Array.isArray(item))) { - throw new Error(`Found array instead of string (tag) or null/falsey, did you forget to \`...\` spread an array or fragment?`); + set tagName(value) { + if (value === undefined || value === null) { + this.tagName = ''; + return; + } + + if (typeof value !== 'string') { + throw new Error(`Expected tagName to be a string`); + } + + if (selfClosingTags.includes(value) && this.content.length) { + throw new Error(`Tag <${value}> is self-closing but this tag has content`); + } + + this.#tagName = value; + } + + get tagName() { + return this.#tagName; + } + + set attributes(attributes) { + if (attributes instanceof Attributes) { + this.#attributes = attributes; + } else { + this.#attributes = new Attributes(attributes); + } + } + + get attributes() { + if (this.#attributes === null) { + this.attributes = {}; + } + + return this.#attributes; + } + + set content(value) { + if ( + this.selfClosing && + !(value === null || + value === undefined || + !Boolean(value) || + Array.isArray(value) && value.filter(Boolean).length === 0) + ) { + throw new Error(`Tag <${this.tagName}> is self-closing but got content`); + } + + let contentArray; + + if (Array.isArray(value)) { + contentArray = value; + } else { + contentArray = [value]; + } + + this.#content = contentArray + .flat(Infinity) + .filter(Boolean); + + this.#content.toString = () => this.#stringifyContent(); + } + + get content() { + if (this.#content === null) { + this.#content = []; + } + + return this.#content; + } + + get selfClosing() { + if (this.tagName) { + return selfClosingTags.includes(this.tagName); + } else { + return false; + } + } + + #setAttributeFlag(attribute, value) { + if (value) { + this.attributes.set(attribute, true); + } else { + this.attributes.remove(attribute); + } + } + + #getAttributeFlag(attribute) { + return !!this.attributes.get(attribute); + } + + #setAttributeString(attribute, value) { + // Note: This function accepts and records the empty string ('') + // distinctly from null/undefined. + + if (value === undefined || value === null) { + this.attributes.remove(attribute); + return undefined; + } else { + this.attributes.set(attribute, String(value)); + } + } + + #getAttributeString(attribute) { + const value = this.attributes.get(attribute); + + if (value === undefined || value === null) { + return undefined; + } else { + return String(value); + } + } + + set onlyIfContent(value) { + this.#setAttributeFlag(onlyIfContent, value); + } + + get onlyIfContent() { + return this.#getAttributeFlag(onlyIfContent); + } + + set joinChildren(value) { + this.#setAttributeString(joinChildren, value); + } + + get joinChildren() { + return this.#getAttributeString(joinChildren); + } + + set noEdgeWhitespace(value) { + this.#setAttributeFlag(noEdgeWhitespace, value); + } + + get noEdgeWhitespace() { + return this.#getAttributeFlag(noEdgeWhitespace); + } + + toString() { + const attributesString = this.attributes.toString(); + const contentString = this.content.toString(); + + if (this.onlyIfContent && !contentString) { + return ''; } - const joiner = attrs?.[joinChildren]; - content = content.filter(Boolean).join( - (joiner === '' + if (!this.tagName) { + return contentString; + } + + const openTag = (attributesString + ? `<${this.tagName} ${attributesString}>` + : `<${this.tagName}>`); + + if (this.selfClosing) { + return openTag; + } + + const closeTag = `</${this.tagName}>`; + + if (!this.content.length) { + return openTag + closeTag; + } + + if (!contentString.includes('\n')) { + return openTag + contentString + closeTag; + } + + const parts = [ + openTag, + contentString + .split('\n') + .map((line, i) => + (i === 0 && this.noEdgeWhitespace + ? line + : ' ' + line)) + .join('\n'), + closeTag, + ]; + + return parts.join( + (this.noEdgeWhitespace ? '' - : (joiner - ? `\n${joiner}\n` - : '\n'))); + : '\n')); } - if (attrs?.[onlyIfContent] && !content) { - return ''; + #stringifyContent() { + if (this.selfClosing) { + return ''; + } + + const joiner = + (this.joinChildren === undefined + ? '\n' + : (this.joinChildren === '' + ? '' + : `\n${this.joinChildren}\n`)); + + return this.content + .map(item => item.toString()) + .filter(Boolean) + .join(joiner); } +} + +export class Attributes { + #attributes = Object.create(null); - if (attrs) { - const attrString = attributes(attrs); - if (attrString) { - openTag = `${tagName} ${attrString}`; + constructor(attributes) { + this.attributes = attributes; + } + + set attributes(value) { + if (value === undefined || value === null) { + this.#attributes = {}; + return; + } + + if (typeof value !== 'object') { + throw new Error(`Expected attributes to be an object`); } + + this.#attributes = Object.create(null); + Object.assign(this.#attributes, value); } - if (!openTag) { - openTag = tagName; + get attributes() { + return this.#attributes; } - if (content) { - if (content.includes('\n')) { - return [ - `<${openTag}>`, - content - .split('\n') - .map((line, i) => - (i === 0 && attrs?.[noEdgeWhitespace] - ? line - : ' ' + line)) - .join('\n'), - `</${tagName}>`, - ].join( - (attrs?.[noEdgeWhitespace] - ? '' - : '\n')); + set(attribute, value) { + if (value === null || value === undefined) { + this.remove(attribute); } else { - return `<${openTag}>${content}</${tagName}>`; + this.#attributes[attribute] = value; } - } else if (selfClosing) { - return `<${openTag}>`; - } else { - return `<${openTag}></${tagName}>`; + return value; + } + + get(attribute) { + return this.#attributes[attribute]; + } + + remove(attribute) { + return delete this.#attributes[attribute]; + } + + toString() { + return Object.entries(this.attributes) + .map(([key, val]) => { + if (typeof val === 'undefined' || val === null) + return [key, val, false]; + else if (typeof val === 'string') + return [key, val, true]; + else if (typeof val === 'boolean') + return [key, val, val]; + else if (typeof val === 'number') + return [key, val.toString(), true]; + else if (Array.isArray(val)) + return [key, val.filter(Boolean).join(' '), val.length > 0]; + else + throw new Error(`Attribute value for ${key} should be primitive or array, got ${typeof val}`); + }) + .filter(([_key, _val, keep]) => keep) + .map(([key, val]) => { + switch (key) { + case 'href': + return [key, encodeURI(val)]; + default: + return [key, val]; + } + }) + .map(([key, val]) => + typeof val === 'boolean' + ? `${key}` + : `${key}="${this.#escapeAttributeValue(val)}"` + ) + .join(' '); + } + + #escapeAttributeValue(value) { + return value + .replaceAll('"', '"') + .replaceAll("'", '''); } } -export function escapeAttributeValue(value) { - return value.replaceAll('"', '"').replaceAll("'", '''); +export function template(description) { + return new Template(description); } -export function attributes(attribs) { - return Object.entries(attribs) - .map(([key, val]) => { - if (typeof val === 'undefined' || val === null) - return [key, val, false]; - else if (typeof val === 'string') - return [key, val, true]; - else if (typeof val === 'boolean') - return [key, val, val]; - else if (typeof val === 'number') - return [key, val.toString(), true]; - else if (Array.isArray(val)) - return [key, val.filter(Boolean).join(' '), val.length > 0]; - else - throw new Error(`Attribute value for ${key} should be primitive or array, got ${typeof val}`); - }) - .filter(([_key, _val, keep]) => keep) - .map(([key, val]) => { - switch (key) { - case 'href': - return [key, encodeURI(val)]; - default: - return [key, val]; +export class Template { + #description = {}; + #slotValues = {}; + + constructor(description) { + Template.validateDescription(description); + this.#description = description; + } + + clone() { + const clone = new Template(this.#description); + clone.setSlots(this.#slotValues); + return clone; + } + + static validateDescription(description) { + if (typeof description !== 'object') { + throw new TypeError(`Expected object, got ${typeof description}`); + } + + if (description === null) { + throw new TypeError(`Expected object, got null`); + } + + const topErrors = []; + + if (!('content' in description)) { + topErrors.push(new TypeError(`Expected description.content`)); + } else if (typeof description.content !== 'function') { + topErrors.push(new TypeError(`Expected description.content to be function`)); + } + + if ('annotation' in description) { + if (typeof description.annotation !== 'string') { + topErrors.push(new TypeError(`Expected annotation to be string`)); } - }) - .map(([key, val]) => - typeof val === 'boolean' - ? `${key}` - : `${key}="${escapeAttributeValue(val)}"` - ) - .join(' '); -} + } + + if ('slots' in description) validateSlots: { + if (typeof description.slots !== 'object') { + topErrors.push(new TypeError(`Expected description.slots to be object`)); + break validateSlots; + } + + const slotErrors = []; -// Ensures the passed value is an array of elements, for usage in [...spread] -// syntax. This may be used when it's not guaranteed whether the return value of -// an external function is one child or an array, or in combination with -// conditionals, e.g. fragment(cond && [x, y, z]). -export function fragment(childOrChildren) { - if (!childOrChildren) { - return []; + for (const [slotName, slotDescription] of Object.entries(description.slots)) { + if (typeof slotDescription !== 'object' || slotDescription === null) { + slotErrors.push(new TypeError(`(${slotName}) Expected slot description to be object`)); + continue; + } + + if ('default' in slotDescription) validateDefault: { + if ( + slotDescription.default === undefined || + slotDescription.default === null + ) { + slotErrors.push(new TypeError(`(${slotName}) Leave slot default unspecified instead of undefined or null`)); + break validateDefault; + } + + try { + Template.validateSlotValueAgainstDescription(slotDescription.default, slotDescription); + } catch (error) { + error.message = `(${slotName}) Error validating slot default value: ${error.message}`; + slotErrors.push(error); + } + } + + if ('validate' in slotDescription && 'type' in slotDescription) { + slotErrors.push(new TypeError(`(${slotName}) Don't specify both slot validate and type`)); + } else if (!('validate' in slotDescription || 'type' in slotDescription)) { + slotErrors.push(new TypeError(`(${slotName}) Expected either slot validate or type`)); + } else if ('validate' in slotDescription) { + if (typeof slotDescription.validate !== 'function') { + slotErrors.push(new TypeError(`(${slotName}) Expected slot validate to be function`)); + } + } else if ('type' in slotDescription) { + const acceptableSlotTypes = [ + 'string', + 'number', + 'bigint', + 'boolean', + 'symbol', + 'html', + ]; + + if (slotDescription.type === 'function') { + slotErrors.push(new TypeError(`(${slotName}) Functions shouldn't be provided to slots`)); + } else if (slotDescription.type === 'object') { + slotErrors.push(new TypeError(`(${slotName}) Provide validate function instead of type: object`)); + } else if (!acceptableSlotTypes.includes(slotDescription.type)) { + slotErrors.push(new TypeError(`(${slotName}) Expected slot type to be one of ${acceptableSlotTypes.join(', ')}`)); + } + } + } + + if (!empty(slotErrors)) { + topErrors.push(new AggregateError(slotErrors, `Errors in slot descriptions`)); + } + } + + if (!empty(topErrors)) { + throw new AggregateError(topErrors, + (typeof description.annotation === 'string' + ? `Errors validating template "${description.annotation}" description` + : `Errors validating template description`)); + } + + return true; } - if (Array.isArray(childOrChildren)) { - return childOrChildren; + slot(slotName, value) { + this.setSlot(slotName, value); + return this; } - return [childOrChildren]; + slots(slotNamesToValues) { + this.setSlots(slotNamesToValues); + return this; + } + + setSlot(slotName, value) { + const description = this.#getSlotDescriptionOrError(slotName); + + try { + Template.validateSlotValueAgainstDescription(value, description); + } catch (error) { + error.message = + (this.description.annotation + ? `Error validating template "${this.description.annotation}" slot "${slotName}" value: ${error.message}` + : `Error validating template slot "${slotName}" value: ${error.message}`); + throw error; + } + + this.#slotValues[slotName] = value; + } + + setSlots(slotNamesToValues) { + if ( + typeof slotNamesToValues !== 'object' || + Array.isArray(slotNamesToValues) || + slotNamesToValues === null + ) { + throw new TypeError(`Expected object mapping of slot names to values`); + } + + const slotErrors = []; + + for (const [slotName, value] of Object.entries(slotNamesToValues)) { + const description = this.#getSlotDescriptionNoError(slotName); + if (!description) { + slotErrors.push(new TypeError(`(${slotName}) Template doesn't have a "${slotName}" slot`)); + continue; + } + + try { + Template.validateSlotValueAgainstDescription(value, description); + } catch (error) { + error.message = `(${slotName}) ${error.message}`; + slotErrors.push(error); + } + } + + if (!empty(slotErrors)) { + throw new AggregateError(slotErrors, + (this.description.annotation + ? `Error validating template "${this.description.annotation}" slots` + : `Error validating template slots`)); + } + + Object.assign(this.#slotValues, slotNamesToValues); + } + + static validateSlotValueAgainstDescription(value, description) { + if (value === undefined) { + throw new TypeError(`Specify value as null or don't specify at all`); + } + + // Null is always an acceptable slot value. + if (value !== null) { + if ('validate' in description) { + description.validate({ + ...commonValidators, + ...validators, + })(value); + } + + if ('type' in description) { + const {type} = description; + if (type === 'html') { + if (!isHTML(value)) { + throw new TypeError(`Slot expects html (tag, template or blank), got ${typeof value}`); + } + } else { + if (typeof value !== type) { + throw new TypeError(`Slot expects ${type}, got ${typeof value}`); + } + } + } + } + + return true; + } + + getSlotValue(slotName) { + const description = this.#getSlotDescriptionOrError(slotName); + const providedValue = this.#slotValues[slotName] ?? null; + + if (description.type === 'html') { + if (!providedValue) { + return blank(); + } + + if (providedValue instanceof Tag || providedValue instanceof Template) { + return providedValue.clone(); + } + + return providedValue; + } + + if (providedValue !== null) { + return providedValue; + } + + if ('default' in description) { + return description.default; + } + + return null; + } + + getSlotDescription(slotName) { + return this.#getSlotDescriptionOrError(slotName); + } + + #getSlotDescriptionNoError(slotName) { + if (this.#description.slots) { + if (Object.hasOwn(this.#description.slots, slotName)) { + return this.#description.slots[slotName]; + } + } + + return null; + } + + #getSlotDescriptionOrError(slotName) { + const description = this.#getSlotDescriptionNoError(slotName); + + if (!description) { + throw new TypeError( + (this.description.annotation + ? `Template "${this.description.annotation}" doesn't have a "${slotName}" slot` + : `Template doesn't have a "${slotName}" slot`)); + } + + return description; + } + + set content(_value) { + throw new Error(`Template content can't be changed after constructed`); + } + + get content() { + const slots = {}; + + for (const slotName of Object.keys(this.description.slots ?? {})) { + slots[slotName] = this.getSlotValue(slotName); + } + + return this.description.content(slots); + } + + set description(_value) { + throw new Error(`Template description can't be changed after constructed`); + } + + get description() { + return this.#description; + } + + toString() { + return this.content.toString(); + } } diff --git a/src/util/link.js b/src/util/link.js index 62106345..a9f79c8b 100644 --- a/src/util/link.js +++ b/src/util/link.js @@ -24,23 +24,29 @@ export function unbound_getLinkThemeString(color, { const appendIndexHTMLRegex = /^(?!https?:\/\/).+\/$/; -const linkHelper = - (hrefFn, { - color = true, - attr = null, - } = {}) => - (thing, { +function linkHelper({ + path: pathOption, + + expectThing = true, + color: colorOption = true, + + attr: attrOption = null, + data: dataOption = null, + text: textOption = null, +}) { + const generateLink = (data, { getLinkThemeString, to, text = '', attributes = null, class: className = '', - color: color2 = true, + color = true, hash = '', preferShortName = false, }) => { - let href = hrefFn(thing, {to}); + const path = (expectThing ? pathOption(data) : pathOption()); + let href = to(...path); if (link.globalOptions.appendIndexHTML) { if (appendIndexHTMLRegex.test(href)) { @@ -52,41 +58,100 @@ const linkHelper = href += (hash.startsWith('#') ? '' : '#') + hash; } - return html.tag( - 'a', + return html.tag('a', { - ...(attr ? attr(thing) : {}), + ...(attrOption ? attrOption(data) : {}), ...(attributes ? attributes : {}), href, style: - typeof color2 === 'string' - ? getLinkThemeString(color2) - : color2 && color - ? getLinkThemeString(thing.color) + typeof color === 'string' + ? getLinkThemeString(color) + : color && colorOption + ? getLinkThemeString(data.color) : '', class: className, }, + (text || - (preferShortName - ? thing.nameShort ?? thing.name - : thing.name)) - ); + (textOption + ? textOption(data) + : (preferShortName + ? data.nameShort ?? data.name + : data.name)))); + }; + + generateLink.data = thing => { + if (!expectThing) { + throw new Error(`This kind of link doesn't need any data serialized`); + } + + const data = (dataOption ? dataOption(thing) : {}); + + if (colorOption) { + data.color = thing.color; + } + + if (!textOption) { + data.name = thing.name; + data.nameShort = thing.nameShort ?? thing.name; + } + + return data; }; -const linkDirectory = (key, {expose = null, attr = null, ...conf} = {}) => - linkHelper((thing, {to}) => to('localized.' + key, thing.directory), { - attr: (thing) => ({ - ...(attr ? attr(thing) : {}), - ...(expose ? {[expose]: thing.directory} : {}), + return generateLink; +} + +function linkDirectory(key, { + exposeDirectory = null, + prependLocalized = true, + + data = null, + attr = null, + ...conf +} = {}) { + return linkHelper({ + data: thing => ({ + ...(data ? data(thing) : {}), + directory: thing.directory, }), + + path: data => + (prependLocalized + ? ['localized.' + key, data.directory] + : [key, data.directory]), + + attr: (data) => ({ + ...(attr ? attr(data) : {}), + ...(exposeDirectory ? {[exposeDirectory]: data.directory} : {}), + }), + ...conf, }); +} -const linkPathname = (key, conf) => - linkHelper(({directory: pathname}, {to}) => to(key, pathname), conf); +function linkIndex(key, conf) { + return linkHelper({ + path: () => [key], -const linkIndex = (key, conf) => - linkHelper((_, {to}) => to('localized.' + key), conf); + expectThing: false, + ...conf, + }); +} + +function linkAdditionalFile(key, conf) { + return linkHelper({ + data: ({file, album}) => ({ + directory: album.directory, + file, + }), + + path: data => ['media.albumAdditionalFile', data.directory, data.file], + + color: false, + ...conf, + }); +} // Mapping of Thing constructor classes to the key for a link.x() function. // These represent a sensible "default" link, i.e. to the primary page for @@ -114,6 +179,7 @@ const link = { }, album: linkDirectory('album'), + albumAdditionalFile: linkAdditionalFile('albumAdditionalFile'), albumGallery: linkDirectory('albumGallery'), albumCommentary: linkDirectory('albumCommentary'), artist: linkDirectory('artist', {color: false}), @@ -130,32 +196,26 @@ const link = { newsEntry: linkDirectory('newsEntry', {color: false}), staticPage: linkDirectory('staticPage', {color: false}), tag: linkDirectory('tag'), - track: linkDirectory('track', {expose: 'data-track'}), - - // TODO: This is a bit hacky. Files are just strings (not objects), so we - // have to manually provide the album alongside the file. They also don't - // follow the usual {name: whatever} type shape, so we have to provide that - // ourselves. - _albumAdditionalFileHelper: linkHelper( - (fakeFileObject, {to}) => - to( - 'media.albumAdditionalFile', - fakeFileObject.album.directory, - fakeFileObject.name), - {color: false}), - - albumAdditionalFile: ({file, album}, {to, ...opts}) => - link._albumAdditionalFileHelper( - { - name: file, - album, - }, - {to, ...opts}), - - media: linkPathname('media.path', {color: false}), - root: linkPathname('shared.path', {color: false}), - data: linkPathname('data.path', {color: false}), - site: linkPathname('localized.path', {color: false}), + track: linkDirectory('track', {exposeDirectory: 'data-track'}), + + media: linkDirectory('media.path', { + prependLocalized: false, + color: false, + }), + + root: linkDirectory('shared.path', { + prependLocalized: false, + color: false, + }), + data: linkDirectory('data.path', { + prependLocalized: false, + color: false, + }), + + site: linkDirectory('localized.path', { + prependLocalized: false, + color: false, + }), // This is NOT an arrow functions because it should be callable for other // "this" objects - i.e, if we bind arguments in other functions on the same diff --git a/src/util/replacer.js b/src/util/replacer.js index ea957eda..50a90004 100644 --- a/src/util/replacer.js +++ b/src/util/replacer.js @@ -221,11 +221,10 @@ function parseNodes(input, i, stopAt, textOnly) { let hash; if (stop_literal === tagHash) { - N = parseNodes(input, i, [R_tagArgument, R_tagLabel, R_tagEnding]); + N = parseOneTextNode(input, i, [R_tagArgument, R_tagLabel, R_tagEnding]); if (!stopped) throw endOfInput(i, `reading hash`); - - if (!N) throw makeError(i, `Expected content (hash).`); + if (!N) throw makeError(i, `Expected text (hash).`); hash = N; i = stop_iParse; @@ -294,6 +293,10 @@ function parseNodes(input, i, stopAt, textOnly) { } export function parseInput(input) { + if (typeof input !== 'string') { + throw new TypeError(`Expected input to be string, got ${input}`); + } + try { return parseNodes(input, 0); } catch (errorNode) { @@ -378,7 +381,7 @@ function evaluateTag(node, opts) { (transformName && transformName(value.name, node, input)) || null; - const hash = node.data.hash && transformNodes(node.data.hash, opts); + const hash = node.data.hash && transformNode(node.data.hash, opts); const args = node.data.args && diff --git a/src/util/sugar.js b/src/util/sugar.js index 0813c1d4..6ab70bc6 100644 --- a/src/util/sugar.js +++ b/src/util/sugar.js @@ -82,6 +82,14 @@ export const compareArrays = (arr1, arr2, {checkOrder = true} = {}) => export const withEntries = (obj, fn) => Object.fromEntries(fn(Object.entries(obj))); +export function filterProperties(obj, properties) { + const set = new Set(properties); + return Object.fromEntries( + Object + .entries(obj) + .filter(([key]) => set.has(key))); +} + export function queue(array, max = 50) { if (max === 0) { return array.map((fn) => fn()); @@ -146,10 +154,20 @@ export function bindOpts(fn, bind) { ]); }; - Object.defineProperty(bound, 'name', { - value: fn.name ? `(options-bound) ${fn.name}` : `(options-bound)`, + annotateFunction(bound, { + name: fn, + trait: 'options-bound', }); + for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(fn))) { + if (key === 'length') continue; + if (key === 'name') continue; + if (key === 'arguments') continue; + if (key === 'caller') continue; + if (key === 'prototype') continue; + Object.defineProperty(bound, key, descriptor); + } + return bound; } @@ -216,6 +234,10 @@ export function openAggregate({ ); }; + aggregate.push = (error) => { + errors.push(error); + }; + aggregate.call = (fn, ...args) => { return aggregate.wrap(fn)(...args); }; @@ -421,6 +443,7 @@ export function _withAggregate(mode, aggregateOpts, fn) { export function showAggregate(topError, { pathToFileURL = f => f, showTraces = true, + print = true, } = {}) { const recursive = (error, {level}) => { let header = showTraces @@ -465,7 +488,13 @@ export function showAggregate(topError, { } }; - console.error(recursive(topError, {level: 0})); + const message = recursive(topError, {level: 0}); + + if (print) { + console.error(message); + } else { + return message; + } } export function decorateErrorWithIndex(fn) { @@ -478,3 +507,74 @@ export function decorateErrorWithIndex(fn) { } }; } + +// Delicious function annotations, such as: +// +// (*bound) soWeAreBackInTheMine +// (data *unfulfilled) generateShrekTwo +// +export function annotateFunction(fn, { + name: nameOrFunction = null, + description: newDescription, + trait: newTrait, +}) { + let name; + + if (typeof nameOrFunction === 'function') { + name = nameOrFunction.name; + } else if (typeof nameOrFunction === 'string') { + name = nameOrFunction; + } + + name ??= fn.name ?? 'anonymous'; + + const match = name.match(/^ *(?<prefix>.*?) *\((?<description>.*)( #(?<trait>.*))?\) *(?<suffix>.*) *$/); + + let prefix, suffix, description, trait; + if (match) { + ({prefix, suffix, description, trait} = match.groups); + } + + prefix ??= ''; + suffix ??= name; + description ??= ''; + trait ??= ''; + + if (newDescription) { + if (description) { + description += '; ' + newDescription; + } else { + description = newDescription; + } + } + + if (newTrait) { + if (trait) { + trait += ' #' + newTrait; + } else { + trait = '#' + newTrait; + } + } + + let parenthesesPart; + + if (description && trait) { + parenthesesPart = `${description} ${trait}`; + } else if (description || trait) { + parenthesesPart = description || trait; + } else { + parenthesesPart = ''; + } + + let finalName; + + if (prefix && parenthesesPart) { + finalName = `${prefix} (${parenthesesPart}) ${suffix}`; + } else if (parenthesesPart) { + finalName = `(${parenthesesPart}) ${suffix}`; + } else { + finalName = suffix; + } + + Object.defineProperty(fn, 'name', {value: finalName}); +} diff --git a/src/util/transform-content.js b/src/util/transform-content.js index d1d0f51a..454cb374 100644 --- a/src/util/transform-content.js +++ b/src/util/transform-content.js @@ -3,7 +3,6 @@ // interfaces for converting various content found in wiki data to HTML for // display on the site. -import * as html from './html.js'; export {transformInline} from './replacer.js'; export const replacerSpec = { @@ -34,7 +33,7 @@ export const replacerSpec = { date: { find: null, value: (ref) => new Date(ref), - html: (date, {language}) => + html: (date, {html, language}) => html.tag('time', {datetime: date.toString()}, language.formatDate(date)), diff --git a/src/write/bind-utilities.js b/src/write/bind-utilities.js index ffaaa7a7..d6053353 100644 --- a/src/write/bind-utilities.js +++ b/src/write/bind-utilities.js @@ -5,59 +5,28 @@ import chroma from 'chroma-js'; import { - fancifyFlashURL, - fancifyURL, - getAlbumGridHTML, - getAlbumStylesheet, - getArtistString, - getCarouselHTML, - getFlashGridHTML, - getGridHTML, - getRevealStringFromArtTags, - getRevealStringFromContentWarningMessage, - getThemeString, - generateAdditionalFilesList, - generateAdditionalFilesShortcut, - generateChronologyLinks, - generateContentHeading, - generateCoverLink, - generateInfoGalleryLinks, - generateTrackListDividedByGroups, - generateNavigationLinks, - generateStickyHeadingContainer, - iconifyURL, - img, -} from '../misc-templates.js'; - -import { replacerSpec, transformInline, - transformLyrics, - transformMultiline, + // transformLyrics, + // transformMultiline, } from '../util/transform-content.js'; import * as html from '../util/html.js'; -import {bindOpts, withEntries} from '../util/sugar.js'; +import {bindOpts} from '../util/sugar.js'; import {getColors} from '../util/colors.js'; import {bindFind} from '../util/find.js'; - -import link, {getLinkThemeString} from '../util/link.js'; - -import { - getAlbumCover, - getArtistAvatar, - getFlashCover, - getTrackCover, -} from '../util/wiki-data.js'; +import {thumb} from '../util/urls.js'; export function bindUtilities({ absoluteTo, + cachebust, defaultLanguage, getSizeOfAdditionalFile, getSizeOfImageFile, language, languages, + pagePath, to, urls, wikiData, @@ -69,42 +38,22 @@ export function bindUtilities({ Object.assign(bound, { absoluteTo, + cachebust, defaultLanguage, getSizeOfAdditionalFile, getSizeOfImageFile, html, language, languages, + pagePath, + thumb, to, urls, wikiData, - }) - - bound.img = bindOpts(img, { - [bindOpts.bindIndex]: 0, - getSizeOfImageFile, - html, - to, - }); - - bound.getColors = bindOpts(getColors, { - chroma, - }); - - bound.getLinkThemeString = bindOpts(getLinkThemeString, { - getColors: bound.getColors, + wikiInfo: wikiData.wikiInfo, }); - bound.getThemeString = bindOpts(getThemeString, { - getColors: bound.getColors, - }); - - bound.link = withEntries(link, (entries) => - entries - .map(([key, fn]) => [key, bindOpts(fn, { - getLinkThemeString: bound.getLinkThemeString, - to, - })])); + bound.getColors = bindOpts(getColors, {chroma}); bound.find = bindFind(wikiData, {mode: 'warn'}); @@ -117,6 +66,7 @@ export function bindUtilities({ wikiData, }); + /* bound.transformMultiline = bindOpts(transformMultiline, { img: bound.img, to, @@ -127,81 +77,14 @@ export function bindUtilities({ transformInline: bound.transformInline, transformMultiline: bound.transformMultiline, }); + */ - bound.iconifyURL = bindOpts(iconifyURL, { - html, - language, - to, - }); - - bound.fancifyURL = bindOpts(fancifyURL, { - html, - language, - }); - - bound.fancifyFlashURL = bindOpts(fancifyFlashURL, { - [bindOpts.bindIndex]: 2, - html, - language, - - fancifyURL: bound.fancifyURL, - }); - - bound.getRevealStringFromContentWarningMessage = bindOpts(getRevealStringFromContentWarningMessage, { - html, - language, - }); - - bound.getRevealStringFromArtTags = bindOpts(getRevealStringFromArtTags, { - language, - - getRevealStringFromContentWarningMessage: bound.getRevealStringFromContentWarningMessage, - }); - - bound.getArtistString = bindOpts(getArtistString, { - html, - link: bound.link, - language, - - iconifyURL: bound.iconifyURL, - }); - - bound.getAlbumCover = bindOpts(getAlbumCover, { - to, - }); - - bound.getTrackCover = bindOpts(getTrackCover, { - to, - }); - - bound.getFlashCover = bindOpts(getFlashCover, { - to, - }); - - bound.getArtistAvatar = bindOpts(getArtistAvatar, { - to, - }); - - bound.generateAdditionalFilesShortcut = bindOpts(generateAdditionalFilesShortcut, { - html, - language, - }); - - bound.generateAdditionalFilesList = bindOpts(generateAdditionalFilesList, { - html, - language, - }); - + /* bound.generateNavigationLinks = bindOpts(generateNavigationLinks, { link: bound.link, language, }); - bound.generateContentHeading = bindOpts(generateContentHeading, { - [bindOpts.bindIndex]: 0, - html, - }); - bound.generateStickyHeadingContainer = bindOpts(generateStickyHeadingContainer, { [bindOpts.bindIndex]: 0, html, @@ -217,30 +100,12 @@ export function bindUtilities({ generateNavigationLinks: bound.generateNavigationLinks, }); - bound.generateCoverLink = bindOpts(generateCoverLink, { - [bindOpts.bindIndex]: 0, - html, - img: bound.img, - link: bound.link, - language, - to, - wikiData, - - getRevealStringFromArtTags: bound.getRevealStringFromArtTags, - }); - bound.generateInfoGalleryLinks = bindOpts(generateInfoGalleryLinks, { [bindOpts.bindIndex]: 2, link: bound.link, language, }); - bound.generateTrackListDividedByGroups = bindOpts(generateTrackListDividedByGroups, { - html, - language, - wikiData, - }); - bound.getGridHTML = bindOpts(getGridHTML, { [bindOpts.bindIndex]: 0, img: bound.img, @@ -271,11 +136,8 @@ export function bindUtilities({ [bindOpts.bindIndex]: 0, img: bound.img, html, - }) - - bound.getAlbumStylesheet = bindOpts(getAlbumStylesheet, { - to, }); + */ return bound; } diff --git a/src/write/build-modes/live-dev-server.js b/src/write/build-modes/live-dev-server.js index 6dfa7d71..10b40cf0 100644 --- a/src/write/build-modes/live-dev-server.js +++ b/src/write/build-modes/live-dev-server.js @@ -11,7 +11,7 @@ import {serializeThings} from '../../data/serialize.js'; import * as pageSpecs from '../../page/index.js'; import {logInfo, logWarn, progressCallAll} from '../../util/cli.js'; - +import {empty} from '../../util/sugar.js'; import { getPagePathname, getPagePathnameAcrossLanguages, @@ -20,11 +20,21 @@ import { } from '../../util/urls.js'; import { - generateDocumentHTML, generateGlobalWikiDataJSON, generateRedirectHTML, } from '../page-template.js'; +import { + watchContentDependencies, +} from '../../content/dependencies/index.js'; + +import { + fillRelationsLayoutFromSlotResults, + flattenRelationsTree, + getRelationsTree, + getNeededContentDependencyNames, +} from '../../content-function.js'; + const defaultHost = '0.0.0.0'; const defaultPort = 8002; @@ -64,20 +74,35 @@ export async function go({ developersComment, getSizeOfAdditionalFile, getSizeOfImageFile, + niceShowAggregate, }) { + const showError = (error) => { + if (error instanceof AggregateError && niceShowAggregate) { + niceShowAggregate(error); + } else { + console.error(error); + } + }; + const host = cliOptions['host'] ?? defaultHost; const port = parseInt(cliOptions['port'] ?? defaultPort); + const contentDependenciesWatcher = await watchContentDependencies(); + const {contentDependencies: allContentDependencies} = contentDependenciesWatcher; + + contentDependenciesWatcher.on('error', () => {}); + await new Promise(resolve => contentDependenciesWatcher.once('ready', resolve)); + let targetSpecPairs = getPageSpecsWithTargets({wikiData}); const pages = progressCallAll(`Computing page data & paths for ${targetSpecPairs.length} targets.`, - targetSpecPairs.map(({ + targetSpecPairs.flatMap(({ pageSpec, target, targetless, }) => () => targetless - ? pageSpec.writeTargetless({wikiData}) - : pageSpec.write(target, {wikiData}))).flat(); + ? [pageSpec.writeTargetless({wikiData})] + : pageSpec.pathsForTarget(target))).flat(); logInfo`Will be serving a total of ${pages.length} pages.`; @@ -143,7 +168,7 @@ export async function go({ response.writeHead(500, contentTypeJSON); response.end({error: `Internal error serializing wiki JSON`}); console.error(`${requestHead} [500] /data.json`); - console.error(error); + showError(error); } return; } @@ -186,7 +211,7 @@ export async function go({ response.writeHead(500, contentTypePlain); response.end(`Internal error accessing ${localFileArea} file for: ${safePath}`); console.error(`${requestHead} [500] ${pathname}`); - console.error(error); + showError(error); } return; } @@ -239,7 +264,7 @@ export async function go({ response.writeHead(500, contentTypePlain); response.end(`Failed during file-to-response pipeline`); console.error(`${requestHead} [500] ${pathname}`); - console.error(error); + showError(error); } return; } @@ -305,8 +330,6 @@ export async function go({ return; } - response.writeHead(200, contentTypeHTML); - const localizedPathnames = getPagePathnameAcrossLanguages({ defaultLanguage, languages, @@ -314,37 +337,139 @@ export async function go({ urls, }); + const {name, args} = page.contentFunction; + const bound = bindUtilities({ absoluteTo, + cachebust, defaultLanguage, getSizeOfAdditionalFile, getSizeOfImageFile, language, languages, + pagePath: servePath, to, urls, wikiData, }); - const pageInfo = page.page(bound); - - const pageHTML = generateDocumentHTML(pageInfo, { + const allExtraDependencies = { ...bound, - cachebust, - developersComment, - localizedPathnames, - oEmbedJSONHref: null, // No oEmbed support for live dev server - pagePath: servePath, - pathname, - }); + + appendIndexHTML: false, + transformMultiline: text => `<p>${text}</p>`, + }; + + // NOTE: ALL THIS STUFF IS PASTED, REVIEW AND INTEGRATE SOON(TM) + + const treeInfo = getRelationsTree(allContentDependencies, name, wikiData, ...args); + const flatTreeInfo = flattenRelationsTree(treeInfo); + const {root, relationIdentifier, flatRelationSlots} = flatTreeInfo; + + const neededContentDependencyNames = + getNeededContentDependencyNames(allContentDependencies, name); + + // Content functions aren't recursive, so by following the set above + // sequentually, we will always provide fulfilled content functions as the + // dependencies for later content functions. + const fulfilledContentDependencies = {}; + for (const name of neededContentDependencyNames) { + const unfulfilledContentFunction = allContentDependencies[name]; + if (!unfulfilledContentFunction) continue; + + const {contentDependencies, extraDependencies} = unfulfilledContentFunction; + + if (empty(contentDependencies) && empty(extraDependencies)) { + fulfilledContentDependencies[name] = unfulfilledContentFunction; + continue; + } + + const fulfillments = {}; + + for (const dependencyName of contentDependencies ?? []) { + if (dependencyName in fulfilledContentDependencies) { + fulfillments[dependencyName] = + fulfilledContentDependencies[dependencyName]; + } + } + + for (const dependencyName of extraDependencies ?? []) { + if (dependencyName in allExtraDependencies) { + fulfillments[dependencyName] = + allExtraDependencies[dependencyName]; + } + } + + fulfilledContentDependencies[name] = + unfulfilledContentFunction.fulfill(fulfillments); + } + + // There might still be unfulfilled content functions if dependencies weren't + // provided as part of allContentDependencies or allExtraDependencies. + // Catch and report these early, together in an aggregate error. + const unfulfilledErrors = []; + const unfulfilledNames = []; + for (const name of neededContentDependencyNames) { + const contentFunction = fulfilledContentDependencies[name]; + if (!contentFunction) continue; + if (!contentFunction.fulfilled) { + try { + contentFunction(); + } catch (error) { + error.message = `(${name}) ${error.message}`; + unfulfilledErrors.push(error); + unfulfilledNames.push(name); + } + } + } + + if (!empty(unfulfilledErrors)) { + throw new AggregateError(unfulfilledErrors, `Content functions unfulfilled (${unfulfilledNames.join(', ')})`); + } + + const slotResults = {}; + + function runContentFunction({name, args, relations: flatRelations}) { + const contentFunction = fulfilledContentDependencies[name]; + + if (!contentFunction) { + throw new Error(`Content function ${name} unfulfilled or not listed`); + } + + const sprawl = + contentFunction.sprawl?.(allExtraDependencies.wikiData, ...args); + + const relations = + fillRelationsLayoutFromSlotResults(relationIdentifier, slotResults, flatRelations); + + const data = + (sprawl + ? contentFunction.data?.(sprawl, ...args) + : contentFunction.data?.(...args)); + + const generateArgs = [data, relations].filter(Boolean); + + return contentFunction(...generateArgs); + } + + for (const slot of Object.getOwnPropertySymbols(flatRelationSlots)) { + slotResults[slot] = runContentFunction(flatRelationSlots[slot]); + } + + const topLevelResult = runContentFunction(root); + + // END PASTE + + const pageHTML = topLevelResult.toString(); console.log(`${requestHead} [200] ${pathname}`); + response.writeHead(200, contentTypeHTML); response.end(pageHTML); } catch (error) { response.writeHead(500, contentTypePlain); response.end(`Error generating page, view server log for details\n`); console.error(`${requestHead} [500] ${pathname}`); - console.error(error); + showError(error); } }); @@ -360,7 +485,7 @@ export async function go({ }, 10_000); } else { console.error(`Server error detected (code: ${error.code})`); - console.error(error); + showError(error); } }); diff --git a/src/write/page-template.js b/src/write/page-template.js index 8a3b44e8..d3d7b098 100644 --- a/src/write/page-template.js +++ b/src/write/page-template.js @@ -3,11 +3,6 @@ import chroma from 'chroma-js'; import * as html from '../util/html.js'; import {getColors} from '../util/colors.js'; -import { - getFooterLocalizationLinks, - getRevealStringFromContentWarningMessage, -} from '../misc-templates.js'; - export function generateDevelopersCommentHTML({ buildTime, commit, @@ -153,40 +148,7 @@ export function generateDocumentHTML(pageInfo, { const collapseSidebars = sidebarLeft.collapse !== false && sidebarRight.collapse !== false; - const mainHTML = - html.tag('main', { - id: 'content', - class: main.classes, - }, [ - ...html.fragment( - !title ? - null - : main.headingMode === 'sticky' ? - generateStickyHeadingContainer({ - coverSrc: cover.src, - coverAlt: cover.alt, - coverArtTags: cover.artTags, - title, - }) - : main.headingMode === 'static' ? - html.tag('h1', title) - : null), - - ...html.fragment( - cover.src && - generateCoverLink({ - src: cover.src, - alt: cover.alt, - tags: cover.artTags, - })), - html.tag('div', - { - [html.onlyIfContent]: true, - class: 'main-content-container', - }, - main.content), - ]); const footerHTML = html.tag('footer', @@ -378,31 +340,6 @@ export function generateDocumentHTML(pageInfo, { height: banner.dimensions[1] || 200, })); - const layoutHTML = [ - navHTML, - banner.position === 'top' && bannerHTML, - secondaryNavHTML, - html.tag('div', - { - class: [ - 'layout-columns', - !collapseSidebars && 'vertical-when-thin', - (sidebarLeftHTML || sidebarRightHTML) && 'has-one-sidebar', - (sidebarLeftHTML && sidebarRightHTML) && 'has-two-sidebars', - !(sidebarLeftHTML || sidebarRightHTML) && 'has-zero-sidebars', - sidebarLeftHTML && 'has-sidebar-left', - sidebarRightHTML && 'has-sidebar-right', - ], - }, - [ - sidebarLeftHTML, - mainHTML, - sidebarRightHTML, - ]), - banner.position === 'bottom' && bannerHTML, - footerHTML, - ].filter(Boolean).join('\n'); - const processSkippers = skipperList => skipperList .filter(Boolean) @@ -612,92 +549,7 @@ export function generateDocumentHTML(pageInfo, { }), ].filter(Boolean).join('\n'); - return `<!DOCTYPE html>\n` + html.tag('html', - { - lang: language.intlCode, - 'data-language-code': language.code, - 'data-url-key': 'localized.' + pagePath[0], - ...Object.fromEntries( - pagePath.slice(1).map((v, i) => [['data-url-value' + i], v])), - 'data-rebase-localized': to('localized.root'), - 'data-rebase-shared': to('shared.root'), - 'data-rebase-media': to('media.root'), - 'data-rebase-data': to('data.root'), - }, - [ - developersComment, - - html.tag('head', [ - html.tag('title', - showWikiNameInTitle - ? language.formatString('misc.pageTitle.withWikiName', { - title, - wikiName: wikiInfo.nameShort, - }) - : language.formatString('misc.pageTitle', {title})), - - html.tag('meta', {charset: 'utf-8'}), - html.tag('meta', { - name: 'viewport', - content: 'width=device-width, initial-scale=1', - }), - - ...( - Object.entries(meta) - .filter(([key, value]) => value) - .map(([key, value]) => html.tag('meta', {[key]: value}))), - - canonical && - html.tag('link', { - rel: 'canonical', - href: canonical, - }), - - ...( - localizedCanonical - .map(({lang, href}) => html.tag('link', { - rel: 'alternate', - hreflang: lang, - href, - }))), - - socialEmbedHTML, - - html.tag('link', { - rel: 'stylesheet', - href: to('shared.staticFile', `site3.css?${cachebust}`), - }), - - html.tag('style', - {[html.onlyIfContent]: true}, - [ - theme, - stylesheet, - ]), - - html.tag('script', { - src: to('shared.staticFile', `lazy-loading.js?${cachebust}`), - }), - ]), - - html.tag('body', - {style: body.style || ''}, - [ - html.tag('div', {id: 'page-container'}, [ - mainHTML && - skippersHTML, - layoutHTML, - ]), - - infoCardHTML, - imageOverlayHTML, - - html.tag('script', { - type: 'module', - src: to('shared.staticFile', `client.js?${cachebust}`), - }), - ]), - ]); + return `<!DOCTYPE html>\n` } export function generateOEmbedJSON(pageInfo, {language, wikiData}) { diff --git a/tap-snapshots/test/snapshot/generateAdditionalFilesList.js.test.cjs b/tap-snapshots/test/snapshot/generateAdditionalFilesList.js.test.cjs new file mode 100644 index 00000000..5ca63482 --- /dev/null +++ b/tap-snapshots/test/snapshot/generateAdditionalFilesList.js.test.cjs @@ -0,0 +1,29 @@ +/* IMPORTANT + * This snapshot file is auto-generated, but designed for humans. + * It should be checked into source control and tracked carefully. + * Re-generate by setting TAP_SNAPSHOT=1 and running tests. + * Make sure to inspect the output below. Do not ignore changes! + */ +'use strict' +exports[`test/snapshot/generateAdditionalFilesList.js TAP generateAdditionalFilesList (snapshot) > basic behavior 1`] = ` +<dl> + <dt>SBURB Wallpaper</dt> + <dd> + <ul> + <li>link to 1280x1024 (2.5 kB)</li> + <li>link to 1440x900</li> + </ul> + </dd> + <dt>Alternate Covers: This is just an example description.</dt> + <dd> + <ul> + <li>link to alt1 (1.2 MB)</li> + <li>link to alt3 (1.2 MB)</li> + </ul> + </dd> +</dl> +` + +exports[`test/snapshot/generateAdditionalFilesList.js TAP generateAdditionalFilesList (snapshot) > no additional files 1`] = ` + +` diff --git a/tap-snapshots/test/snapshot/generateAdditionalFilesShortcut.js.test.cjs b/tap-snapshots/test/snapshot/generateAdditionalFilesShortcut.js.test.cjs new file mode 100644 index 00000000..c94371ca --- /dev/null +++ b/tap-snapshots/test/snapshot/generateAdditionalFilesShortcut.js.test.cjs @@ -0,0 +1,14 @@ +/* IMPORTANT + * This snapshot file is auto-generated, but designed for humans. + * It should be checked into source control and tracked carefully. + * Re-generate by setting TAP_SNAPSHOT=1 and running tests. + * Make sure to inspect the output below. Do not ignore changes! + */ +'use strict' +exports[`test/snapshot/generateAdditionalFilesShortcut.js TAP generateAdditionalFilesShortcut (snapshot) > basic behavior 1`] = ` +View <a href="#additional-files">additional files</a>: SBURB Wallpaper, Alternate Covers +` + +exports[`test/snapshot/generateAdditionalFilesShortcut.js TAP generateAdditionalFilesShortcut (snapshot) > no additional files 1`] = ` + +` diff --git a/tap-snapshots/test/snapshot/generateAlbumTrackListItem.js.test.cjs b/tap-snapshots/test/snapshot/generateAlbumTrackListItem.js.test.cjs new file mode 100644 index 00000000..c561d34b --- /dev/null +++ b/tap-snapshots/test/snapshot/generateAlbumTrackListItem.js.test.cjs @@ -0,0 +1,21 @@ +/* IMPORTANT + * This snapshot file is auto-generated, but designed for humans. + * It should be checked into source control and tracked carefully. + * Re-generate by setting TAP_SNAPSHOT=1 and running tests. + * Make sure to inspect the output below. Do not ignore changes! + */ +'use strict' +exports[`test/snapshot/generateAlbumTrackListItem.js TAP generateAlbumTrackListItem (snapshot) > basic behavior 1`] = ` +<li style="--primary-color: #33cc77">(0:54) <a href="track/final-spice/" style="--primary-color: #33cc77; --dim-color: #437854">Final Spice</a> <span class="by">by <a href="artist/toby-fox/">Toby Fox</a> and <a href="artist/james-roach/">James Roach</a></span></li> +` + +exports[`test/snapshot/generateAlbumTrackListItem.js TAP generateAlbumTrackListItem (snapshot) > hide artists if inherited from album 1`] = ` +<li>(_:__) <a href="track/track1/">Same artists, same order</a></li> +<li>(_:__) <a href="track/track2/">Same artists, different order</a></li> +<li>(_:__) <a href="track/track3/">Extra artist</a> <span class="by">by <a href="artist/toby-fox/">Toby Fox</a>, <a href="artist/james-roach/">James Roach</a>, and <a href="artist/clark-powell/">Clark Powell</a></span></li> +<li>(_:__) <a href="track/track4/">Missing artist</a> <span class="by">by <a href="artist/toby-fox/">Toby Fox</a></span></li> +` + +exports[`test/snapshot/generateAlbumTrackListItem.js TAP generateAlbumTrackListItem (snapshot) > zero duration, zero artists 1`] = ` +<li>(_:__) <a href="track/you-have-got-to-be-about-the-most-superficial-commentator-on-con-langues-since-the-idiotic-b-gilson/">You have got to be about the most superficial commentator on con-langues since the idiotic B. Gilson.</a></li> +` diff --git a/tap-snapshots/test/snapshot/generateContributionLinks.js.test.cjs b/tap-snapshots/test/snapshot/generateContributionLinks.js.test.cjs new file mode 100644 index 00000000..0abd2d38 --- /dev/null +++ b/tap-snapshots/test/snapshot/generateContributionLinks.js.test.cjs @@ -0,0 +1,52 @@ +/* IMPORTANT + * This snapshot file is auto-generated, but designed for humans. + * It should be checked into source control and tracked carefully. + * Re-generate by setting TAP_SNAPSHOT=1 and running tests. + * Make sure to inspect the output below. Do not ignore changes! + */ +'use strict' +exports[`test/snapshot/generateContributionLinks.js TAP generateContributionLinks (snapshot) > no accents 1`] = ` +<a href="artist/clark-powell/">Clark Powell</a>, <a href="artist/the-big-baddies/">Grounder & Scratch</a>, and <a href="artist/toby-fox/">Toby Fox</a> +` + +exports[`test/snapshot/generateContributionLinks.js TAP generateContributionLinks (snapshot) > only showContribution 1`] = ` +<a href="artist/clark-powell/">Clark Powell</a>, <a href="artist/the-big-baddies/">Grounder & Scratch</a> (Snooping), and <a href="artist/toby-fox/">Toby Fox</a> (Arrangement) +` + +exports[`test/snapshot/generateContributionLinks.js TAP generateContributionLinks (snapshot) > only showIcons 1`] = ` +<a href="artist/clark-powell/">Clark Powell</a> (<span class="icons"><a href="https://soundcloud.com/plazmataz" class="icon"> + <svg> + <title>SoundCloud</title> + <use href="static/icons.svg#icon-soundcloud"></use> + </svg> + </a></span>), <a href="artist/the-big-baddies/">Grounder & Scratch</a>, and <a href="artist/toby-fox/">Toby Fox</a> (<span class="icons"><a href="https://tobyfox.bandcamp.com/" class="icon"> + <svg> + <title>Bandcamp</title> + <use href="static/icons.svg#icon-bandcamp"></use> + </svg> + </a>, <a href="https://toby.fox/" class="icon"> + <svg> + <title>External (toby.fox)</title> + <use href="static/icons.svg#icon-globe"></use> + </svg> + </a></span>) +` + +exports[`test/snapshot/generateContributionLinks.js TAP generateContributionLinks (snapshot) > showContribution & showIcons 1`] = ` +<a href="artist/clark-powell/">Clark Powell</a> (<span class="icons"><a href="https://soundcloud.com/plazmataz" class="icon"> + <svg> + <title>SoundCloud</title> + <use href="static/icons.svg#icon-soundcloud"></use> + </svg> + </a></span>), <a href="artist/the-big-baddies/">Grounder & Scratch</a> (Snooping), and <a href="artist/toby-fox/">Toby Fox</a> (Arrangement) (<span class="icons"><a href="https://tobyfox.bandcamp.com/" class="icon"> + <svg> + <title>Bandcamp</title> + <use href="static/icons.svg#icon-bandcamp"></use> + </svg> + </a>, <a href="https://toby.fox/" class="icon"> + <svg> + <title>External (toby.fox)</title> + <use href="static/icons.svg#icon-globe"></use> + </svg> + </a></span>) +` diff --git a/tap-snapshots/test/snapshot/image.js.test.cjs b/tap-snapshots/test/snapshot/image.js.test.cjs new file mode 100644 index 00000000..f1512930 --- /dev/null +++ b/tap-snapshots/test/snapshot/image.js.test.cjs @@ -0,0 +1,60 @@ +/* IMPORTANT + * This snapshot file is auto-generated, but designed for humans. + * It should be checked into source control and tracked carefully. + * Re-generate by setting TAP_SNAPSHOT=1 and running tests. + * Make sure to inspect the output below. Do not ignore changes! + */ +'use strict' +exports[`test/snapshot/image.js TAP image (snapshot) > content warnings via tags 1`] = ` +<div class="reveal"> + <div class="image-container"><div class="image-inner-area"><img src="media/album-art/beyond-canon/cover.png"></div></div> + <span class="reveal-text-container"> + <span class="reveal-text"> + cw: too cool for school + <br> + <span class="reveal-interaction">click to show</span> + </span> + </span> +</div> +` + +exports[`test/snapshot/image.js TAP image (snapshot) > id with link 1`] = ` +<a id="banana" class="box image-link" href="foobar"><div class="image-container"><div class="image-inner-area"><img src="foobar"></div></div></a> +` + +exports[`test/snapshot/image.js TAP image (snapshot) > id with square 1`] = ` +<div class="square"><div class="square-content"><div class="image-container"><div class="image-inner-area"><img id="banana" src="foobar"></div></div></div></div> +` + +exports[`test/snapshot/image.js TAP image (snapshot) > id without link 1`] = ` +<div class="image-container"><div class="image-inner-area"><img id="banana" src="foobar"></div></div> +` + +exports[`test/snapshot/image.js TAP image (snapshot) > lazy with square 1`] = ` +<noscript><div class="square"><div class="square-content"><div class="image-container"><div class="image-inner-area"><img src="foobar"></div></div></div></div></noscript> +<div class="square js-hide"><div class="square-content"><div class="image-container"><div class="image-inner-area"><img class="lazy" data-original="foobar"></div></div></div></div> +` + +exports[`test/snapshot/image.js TAP image (snapshot) > link with file size 1`] = ` +<a class="box image-link" href="media/album-art/pingas/cover.png"><div class="image-container"><div class="image-inner-area"><img data-original-size="1000000" src="media/album-art/pingas/cover.png"></div></div></a> +` + +exports[`test/snapshot/image.js TAP image (snapshot) > source missing 1`] = ` +<div class="image-container"><div class="image-inner-area"><div class="image-text-area">Example of missing source message.</div></div></div> +` + +exports[`test/snapshot/image.js TAP image (snapshot) > source via path 1`] = ` +<div class="image-container"><div class="image-inner-area"><img src="media/album-art/beyond-canon/cover.png"></div></div> +` + +exports[`test/snapshot/image.js TAP image (snapshot) > source via src 1`] = ` +<div class="image-container"><div class="image-inner-area"><img src="https://example.com/bananas.gif"></div></div> +` + +exports[`test/snapshot/image.js TAP image (snapshot) > square 1`] = ` +<div class="square"><div class="square-content"><div class="image-container"><div class="image-inner-area"><img src="foobar"></div></div></div></div> +` + +exports[`test/snapshot/image.js TAP image (snapshot) > width & height 1`] = ` +<div class="image-container"><div class="image-inner-area"><img width="600" height="400" src="foobar"></div></div> +` diff --git a/tap-snapshots/test/snapshot/linkArtist.js.test.cjs b/tap-snapshots/test/snapshot/linkArtist.js.test.cjs new file mode 100644 index 00000000..77516f3f --- /dev/null +++ b/tap-snapshots/test/snapshot/linkArtist.js.test.cjs @@ -0,0 +1,14 @@ +/* IMPORTANT + * This snapshot file is auto-generated, but designed for humans. + * It should be checked into source control and tracked carefully. + * Re-generate by setting TAP_SNAPSHOT=1 and running tests. + * Make sure to inspect the output below. Do not ignore changes! + */ +'use strict' +exports[`test/snapshot/linkArtist.js TAP linkArtist (snapshot) > basic behavior 1`] = ` +<a href="artist/toby-fox/">Toby Fox</a> +` + +exports[`test/snapshot/linkArtist.js TAP linkArtist (snapshot) > prefer short name 1`] = ` +<a href="artist/55gore/">55gore</a> +` diff --git a/tap-snapshots/test/snapshot/linkExternal.js.test.cjs b/tap-snapshots/test/snapshot/linkExternal.js.test.cjs new file mode 100644 index 00000000..e4376810 --- /dev/null +++ b/tap-snapshots/test/snapshot/linkExternal.js.test.cjs @@ -0,0 +1,43 @@ +/* IMPORTANT + * This snapshot file is auto-generated, but designed for humans. + * It should be checked into source control and tracked carefully. + * Re-generate by setting TAP_SNAPSHOT=1 and running tests. + * Make sure to inspect the output below. Do not ignore changes! + */ +'use strict' +exports[`test/snapshot/linkExternal.js TAP linkExternal (snapshot) > basic domain matches 1`] = ` +<a href="https://homestuck.bandcamp.com/" class="nowrap">Bandcamp</a> +<a href="https://soundcloud.com/plazmataz" class="nowrap">SoundCloud</a> +<a href="https://aeritus.tumblr.com/" class="nowrap">Tumblr</a> +<a href="https://twitter.com/awkwarddoesart" class="nowrap">Twitter</a> +<a href="https://www.deviantart.com/chesswanderlust-sama" class="nowrap">DeviantArt</a> +<a href="https://en.wikipedia.org/wiki/Haydn_Quartet_(vocal_ensemble)" class="nowrap">Wikipedia</a> +<a href="https://www.poetryfoundation.org/poets/christina-rossetti" class="nowrap">Poetry Foundation</a> +<a href="https://www.instagram.com/levc_egm/" class="nowrap">Instagram</a> +<a href="https://www.patreon.com/CecilyRenns" class="nowrap">Patreon</a> +<a href="https://open.spotify.com/artist/63SNNpNOicDzG3LY82G4q3" class="nowrap">Spotify</a> +<a href="https://buzinkai.newgrounds.com/" class="nowrap">Newgrounds</a> +` + +exports[`test/snapshot/linkExternal.js TAP linkExternal (snapshot) > custom domains for common platforms 1`] = ` +<a href="https://music.solatrus.com/" class="nowrap">music.solatrus.com</a> +<a href="https://types.pl/" class="nowrap">Mastodon (types.pl)</a> +` + +exports[`test/snapshot/linkExternal.js TAP linkExternal (snapshot) > custom matches - type: album 1`] = ` +<a href="https://youtu.be/abc" class="nowrap">YouTube (full album)</a> +<a href="https://youtube.com/watch?v=abc" class="nowrap">YouTube (full album)</a> +<a href="https://youtube.com/Playlist?list=kweh" class="nowrap">YouTube (playlist)</a> +<a href="https://youtu.be/abc" class="nowrap">YouTube</a> +<a href="https://youtu.be/abc?list=kweh" class="nowrap">YouTube</a> +<a href="https://youtube.com/watch?v=abc" class="nowrap">YouTube</a> +<a href="https://youtube.com/watch?v=abc&list=kweh" class="nowrap">YouTube</a> +` + +exports[`test/snapshot/linkExternal.js TAP linkExternal (snapshot) > missing domain (arbitrary local path) 1`] = ` +<a href="/foo/bar/baz.mp3" class="nowrap">Wiki Archive (local upload)</a> +` + +exports[`test/snapshot/linkExternal.js TAP linkExternal (snapshot) > unknown domain (arbitrary world wide web path) 1`] = ` +<a href="https://snoo.ping.as/usual/i/see/" class="nowrap">snoo.ping.as</a> +` diff --git a/tap-snapshots/test/snapshot/linkExternalFlash.js.test.cjs b/tap-snapshots/test/snapshot/linkExternalFlash.js.test.cjs new file mode 100644 index 00000000..d7f6c1c3 --- /dev/null +++ b/tap-snapshots/test/snapshot/linkExternalFlash.js.test.cjs @@ -0,0 +1,18 @@ +/* IMPORTANT + * This snapshot file is auto-generated, but designed for humans. + * It should be checked into source control and tracked carefully. + * Re-generate by setting TAP_SNAPSHOT=1 and running tests. + * Make sure to inspect the output below. Do not ignore changes! + */ +'use strict' +exports[`test/snapshot/linkExternalFlash.js TAP linkExternalFlash (snapshot) > basic behavior 1`] = ` +<span class="nowrap"><a href="https://homestuck.com/story/4109/" class="nowrap">homestuck.com</a> (page 4109)</span> +<span class="nowrap"><a href="https://youtu.be/FDt-SLyEcjI" class="nowrap">YouTube</a> (on any device)</span> +<span class="nowrap"><a href="https://www.bgreco.net/hsflash/006009.html" class="nowrap">www.bgreco.net</a> (HQ Audio)</span> +<span class="nowrap"><a href="https://www.newgrounds.com/portal/view/582345" class="nowrap">Newgrounds</a></span> +` + +exports[`test/snapshot/linkExternalFlash.js TAP linkExternalFlash (snapshot) > secret page 1`] = ` +<span class="nowrap"><a href="https://homestuck.com/story/pony/" class="nowrap">homestuck.com</a> (secret page)</span> +<span class="nowrap"><a href="https://youtu.be/USB1pj6hAjU" class="nowrap">YouTube</a> (on any device)</span> +` diff --git a/tap-snapshots/test/snapshot/linkTemplate.js.test.cjs b/tap-snapshots/test/snapshot/linkTemplate.js.test.cjs new file mode 100644 index 00000000..3c4acb57 --- /dev/null +++ b/tap-snapshots/test/snapshot/linkTemplate.js.test.cjs @@ -0,0 +1,14 @@ +/* IMPORTANT + * This snapshot file is auto-generated, but designed for humans. + * It should be checked into source control and tracked carefully. + * Re-generate by setting TAP_SNAPSHOT=1 and running tests. + * Make sure to inspect the output below. Do not ignore changes! + */ +'use strict' +exports[`test/snapshot/linkTemplate.js TAP linkTemplate (snapshot) > fill many slots 1`] = ` +<a class="dog" id="cat1" href="https://hsmusic.wiki/media/cool%20file.pdf#fooey" style="--primary-color: #123456ff; --dim-color: #12345677">My Cool Link</a> +` + +exports[`test/snapshot/linkTemplate.js TAP linkTemplate (snapshot) > fill path slot & provide appendIndexHTML 1`] = ` +<a href="/c*lzone/myCoolPath/ham/pineapple/tomato/index.html"></a> +` diff --git a/test/lib/content-function.js b/test/lib/content-function.js new file mode 100644 index 00000000..ac527abc --- /dev/null +++ b/test/lib/content-function.js @@ -0,0 +1,148 @@ +import chroma from 'chroma-js'; +import * as path from 'path'; +import {fileURLToPath} from 'url'; + +import mock from './generic-mock.js'; +import {quickEvaluate} from '../../src/content-function.js'; +import {quickLoadContentDependencies} from '../../src/content/dependencies/index.js'; + +import urlSpec from '../../src/url-spec.js'; +import * as html from '../../src/util/html.js'; +import {empty, showAggregate} from '../../src/util/sugar.js'; +import {getColors} from '../../src/util/colors.js'; +import {generateURLs, thumb} from '../../src/util/urls.js'; +import {processLanguageFile} from '../../src/data/language.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +export function testContentFunctions(t, message, fn) { + const urls = generateURLs(urlSpec); + + t.test(message, async t => { + let loadedContentDependencies; + + const language = await processLanguageFile('./src/strings-default.json'); + const mocks = []; + + const evaluate = ({ + from = 'localized.home', + contentDependencies = {}, + extraDependencies = {}, + ...opts + }) => { + if (!loadedContentDependencies) { + throw new Error(`Await .load() before performing tests`); + } + + const {to} = urls.from(from); + + return cleanCatchAggregate(() => { + return quickEvaluate({ + ...opts, + contentDependencies: { + ...contentDependencies, + ...loadedContentDependencies, + }, + extraDependencies: { + html, + language, + thumb, + to, + urls, + appendIndexHTML: false, + getColors: c => getColors(c, {chroma}), + ...extraDependencies, + }, + }); + }); + }; + + evaluate.load = async (opts) => { + if (loadedContentDependencies) { + throw new Error(`Already loaded!`); + } + + loadedContentDependencies = await asyncCleanCatchAggregate(() => + quickLoadContentDependencies({ + logging: false, + ...opts, + })); + }; + + evaluate.snapshot = (...args) => { + if (!loadedContentDependencies) { + throw new Error(`Await .load() before performing tests`); + } + + const [description, opts] = + (typeof args[0] === 'string' + ? args + : ['output', ...args]); + + let result = evaluate(opts); + + if (opts.multiple) { + result = result.map(item => item.toString()).join('\n'); + } else { + result = result.toString(); + } + + t.matchSnapshot(result, description); + }; + + evaluate.mock = (...opts) => { + const {value, close} = mock(...opts); + mocks.push({close}); + return value; + }; + + await fn(t, evaluate); + + if (!empty(mocks)) { + cleanCatchAggregate(() => { + const errors = []; + for (const {close} of mocks) { + try { + close(); + } catch (error) { + errors.push(error); + } + } + if (!empty(errors)) { + throw new AggregateError(errors, `Errors closing mocks`); + } + }); + } + }); +} + +function printAggregate(error) { + if (error instanceof AggregateError) { + const message = showAggregate(error, { + showTraces: true, + print: false, + pathToFileURL: f => path.relative(path.join(__dirname, '../..'), fileURLToPath(f)), + }); + for (const line of message.split('\n')) { + console.error(line); + } + } +} + +function cleanCatchAggregate(fn) { + try { + return fn(); + } catch (error) { + printAggregate(error); + throw error; + } +} + +async function asyncCleanCatchAggregate(fn) { + try { + return await fn(); + } catch (error) { + printAggregate(error); + throw error; + } +} diff --git a/test/lib/generic-mock.js b/test/lib/generic-mock.js new file mode 100644 index 00000000..119f8731 --- /dev/null +++ b/test/lib/generic-mock.js @@ -0,0 +1,314 @@ +import {same} from 'tcompare'; + +import {empty} from '../../src/util/sugar.js'; + +export default function mock(callback) { + const mocks = []; + + const track = callback => (...args) => { + const {value, close} = callback(...args); + mocks.push({close}); + return value; + }; + + const mock = { + function: track(mockFunction), + }; + + return { + value: callback(mock), + close: () => { + const errors = []; + for (const mock of mocks) { + try { + mock.close(); + } catch (error) { + errors.push(error); + } + } + if (!empty(errors)) { + throw new AggregateError(errors, `Errors closing sub-mocks`); + } + }, + }; +} + +export function mockFunction(...args) { + let name = '(anonymous)'; + let behavior = null; + + if (args.length === 2) { + if ( + typeof args[0] === 'string' && + typeof args[1] === 'function' + ) { + name = args[0]; + behavior = args[1]; + } else { + throw new TypeError(`Expected name to be a string`); + } + } else if (args.length === 1) { + if (typeof args[0] === 'string') { + name = args[0]; + } else if (typeof args[0] === 'function') { + behavior = args[0]; + } else if (args[0] !== null) { + throw new TypeError(`Expected string (name), function (behavior), both, or null / no arguments`); + } + } else if (args.length > 2) { + throw new TypeError(`Expected string (name), function (behavior), both, or null / no arguments`); + } + + let currentCallDescription = newCallDescription(); + const allCallDescriptions = [currentCallDescription]; + + const topLevelErrors = []; + let runningCallCount = 0; + let limitCallCount = false; + let markedAsOnce = false; + + const fn = (...args) => { + const description = processCall(...args); + return description.behavior(...args); + }; + + fn.behavior = value => { + if (!(value === null || ( + typeof value === 'function' + ))) { + throw new TypeError(`Expected function or null`); + } + + currentCallDescription.behavior = behavior; + currentCallDescription.described = true; + + return fn; + } + + fn.argumentCount = value => { + if (!(value === null || ( + typeof value === 'number' && + value === parseInt(value) && + value >= 0 + ))) { + throw new TypeError(`Expected whole number or null`); + } + + if (currentCallDescription.argsPattern) { + throw new TypeError(`Unexpected .argumentCount() when .args() has been called`); + } + + currentCallDescription.argsPattern = {length: value}; + currentCallDescription.described = true; + + return fn; + }; + + fn.args = (...args) => { + const value = args[0]; + + if (args.length > 1 || !(value === null || Array.isArray(value))) { + throw new TypeError(`Expected one array or null`); + } + + currentCallDescription.argsPattern = Object.fromEntries( + value + .map((v, i) => v === undefined ? false : [i, v]) + .filter(Boolean) + .concat([['length', value.length]])); + + currentCallDescription.described = true; + + return fn; + }; + + fn.neverCalled = (...args) => { + if (!empty(args)) { + throw new TypeError(`Didn't expect any arguments`); + } + + if (allCallDescriptions[0].described) { + throw new TypeError(`Unexpected .neverCalled() when any descriptions provided`); + } + + limitCallCount = true; + allCallDescriptions.splice(0, allCallDescriptions.length); + + currentCallDescription = new Proxy({}, { + set() { + throw new Error(`Unexpected description when .neverCalled() has been called`); + }, + }); + + return fn; + }; + + fn.once = (...args) => { + if (!empty(args)) { + throw new TypeError(`Didn't expect any arguments`); + } + + if (allCallDescriptions.length > 1) { + throw new TypeError(`Unexpected .once() when providing multiple descriptions`); + } + + currentCallDescription.described = true; + limitCallCount = true; + markedAsOnce = true; + + return fn; + }; + + fn.next = (...args) => { + if (!empty(args)) { + throw new TypeError(`Didn't expect any arguments`); + } + + if (markedAsOnce) { + throw new TypeError(`Unexpected .next() when .once() has been called`); + } + + currentCallDescription = newCallDescription(); + allCallDescriptions.push(currentCallDescription); + + limitCallCount = true; + + return fn; + }; + + fn.repeat = times => { + // Note: This function should be called AFTER filling out the + // call description which is being repeated. + + if (!( + typeof times === 'number' && + times === parseInt(times) && + times >= 2 + )) { + throw new TypeError(`Expected whole number of at least 2`); + } + + if (markedAsOnce) { + throw new TypeError(`Unexpected .repeat() when .once() has been called`); + } + + // The current call description is already in the full list, + // so skip the first push. + for (let i = 2; i <= times; i++) { + allCallDescriptions.push(currentCallDescription); + } + + // Prep a new description like when calling .next(). + currentCallDescription = newCallDescription(); + allCallDescriptions.push(currentCallDescription); + + limitCallCount = true; + + return fn; + }; + + return { + value: fn, + close: () => { + const totalCallCount = runningCallCount; + const expectedCallCount = countDescribedCalls(); + + if (limitCallCount && totalCallCount !== expectedCallCount) { + if (expectedCallCount > 1) { + topLevelErrors.push(new Error(`Expected ${expectedCallCount} calls, got ${totalCallCount}`)); + } else if (expectedCallCount === 1) { + topLevelErrors.push(new Error(`Expected 1 call, got ${totalCallCount}`)); + } else { + topLevelErrors.push(new Error(`Expected no calls, got ${totalCallCount}`)); + } + } + + if (topLevelErrors.length) { + throw new AggregateError(topLevelErrors, `Errors in mock ${name}`); + } + }, + }; + + function newCallDescription() { + return { + described: false, + behavior: behavior ?? null, + argumentCount: null, + argsPattern: null, + }; + } + + function processCall(...args) { + const callErrors = []; + + runningCallCount++; + + // No further processing, this indicates the function shouldn't have been + // called at all and there aren't any descriptions to match this call with. + if (empty(allCallDescriptions)) { + return newCallDescription(); + } + + const currentCallNumber = runningCallCount; + const currentDescription = selectCallDescription(currentCallNumber); + + const { + argumentCount, + argsPattern, + } = currentDescription; + + if (argumentCount !== null && args.length !== argumentCount) { + callErrors.push( + new Error(`Argument count mismatch: expected ${argumentCount}, got ${args.length}`)); + } + + if (argsPattern !== null) { + const keysToCheck = Object.keys(argsPattern); + const argsAsObject = Object.fromEntries( + args + .map((v, i) => [i.toString(), v]) + .filter(([i]) => keysToCheck.includes(i)) + .concat([['length', args.length]])); + + const {match, diff} = same(argsAsObject, argsPattern); + if (!match) { + callErrors.push(new Error(`Argument pattern mismatch:\n` + diff)); + } + } + + if (!empty(callErrors)) { + const aggregate = new AggregateError(callErrors, `Errors in call #${currentCallNumber}`); + topLevelErrors.push(aggregate); + } + + return currentDescription; + } + + function selectCallDescription(currentCallNumber) { + if (currentCallNumber > countDescribedCalls()) { + const lastDescription = lastCallDescription(); + if (lastDescription.described) { + return newCallDescription(); + } else { + return lastDescription; + } + } else { + return allCallDescriptions[currentCallNumber - 1]; + } + } + + function countDescribedCalls() { + if (empty(allCallDescriptions)) { + return 0; + } + + return ( + (lastCallDescription().described + ? allCallDescriptions.length + : allCallDescriptions.length - 1)); + } + + function lastCallDescription() { + return allCallDescriptions[allCallDescriptions.length - 1]; + } +} diff --git a/test/lib/strict-match-error.js b/test/lib/strict-match-error.js new file mode 100644 index 00000000..e3b36e93 --- /dev/null +++ b/test/lib/strict-match-error.js @@ -0,0 +1,50 @@ +export function strictlyThrows(t, fn, pattern) { + const error = catchErrorOrNull(fn); + + t.currentAssert = strictlyThrows; + + if (error === null) { + t.fail(`expected to throw`); + return; + } + + const nameAndMessage = `${pattern.constructor.name} ${pattern.message}`; + t.match( + prepareErrorForMatch(error), + prepareErrorForMatch(pattern), + (pattern instanceof AggregateError + ? `expected to throw: ${nameAndMessage} (${pattern.errors.length} error(s))` + : `expected to throw: ${nameAndMessage}`)); +} + +function prepareErrorForMatch(error) { + if (error instanceof RegExp) { + return { + message: error, + }; + } + + if (!(error instanceof Error)) { + return error; + } + + const matchable = { + name: error.constructor.name, + message: error.message, + }; + + if (error instanceof AggregateError) { + matchable.errors = error.errors.map(prepareErrorForMatch); + } + + return matchable; +} + +function catchErrorOrNull(fn) { + try { + fn(); + return null; + } catch (error) { + return error; + } +} diff --git a/test/snapshot/generateAdditionalFilesList.js b/test/snapshot/generateAdditionalFilesList.js new file mode 100644 index 00000000..0c27ad19 --- /dev/null +++ b/test/snapshot/generateAdditionalFilesList.js @@ -0,0 +1,63 @@ +import t from 'tap'; +import {testContentFunctions} from '../lib/content-function.js'; + +testContentFunctions(t, 'generateAdditionalFilesList (snapshot)', async (t, evaluate) => { + await evaluate.load(); + + evaluate.snapshot('no additional files', { + name: 'generateAdditionalFilesList', + args: [[]], + }); + + evaluate.snapshot('basic behavior', { + name: 'generateAdditionalFilesList', + args: [ + [ + { + title: 'SBURB Wallpaper', + files: [ + 'sburbwp_1280x1024.jpg', + 'sburbwp_1440x900.jpg', + 'sburbwp_1920x1080.jpg', + ], + }, + { + title: 'Fake Section', + description: 'Ooo, what happens if there are NO file links provided?', + files: [ + 'oops.mp3', + 'Internet Explorer.gif', + 'daisy.mp3', + ], + }, + { + title: 'Alternate Covers', + description: 'This is just an example description.', + files: [ + 'Homestuck_Vol4_alt1.jpg', + 'Homestuck_Vol4_alt2.jpg', + 'Homestuck_Vol4_alt3.jpg', + ], + }, + ], + ], + postprocess: template => template + .slot('fileLinks', { + 'sburbwp_1280x1024.jpg': 'link to 1280x1024', + 'sburbwp_1440x900.jpg': 'link to 1440x900', + 'sburbwp_1920x1080.jpg': null, + 'Homestuck_Vol4_alt1.jpg': 'link to alt1', + 'Homestuck_Vol4_alt2.jpg': null, + 'Homestuck_Vol4_alt3.jpg': 'link to alt3', + }) + .slot('fileSizes', { + 'sburbwp_1280x1024.jpg': 2500, + 'sburbwp_1440x900.jpg': null, + 'sburbwp_1920x1080.jpg': null, + 'Internet Explorer.gif': 1, + 'Homestuck_Vol4_alt1.jpg': 1234567, + 'Homestuck_Vol4_alt2.jpg': 1234567, + 'Homestuck_Vol4_alt3.jpg': 1234567, + }), + }); +}); diff --git a/test/snapshot/generateAdditionalFilesShortcut.js b/test/snapshot/generateAdditionalFilesShortcut.js new file mode 100644 index 00000000..0ca777b4 --- /dev/null +++ b/test/snapshot/generateAdditionalFilesShortcut.js @@ -0,0 +1,36 @@ +import t from 'tap'; +import {testContentFunctions} from '../lib/content-function.js'; + +testContentFunctions(t, 'generateAdditionalFilesShortcut (snapshot)', async (t, evaluate) => { + await evaluate.load(); + + evaluate.snapshot('no additional files', { + name: 'generateAdditionalFilesShortcut', + args: [[]], + }); + + evaluate.snapshot('basic behavior', { + name: 'generateAdditionalFilesShortcut', + args: [ + [ + { + title: 'SBURB Wallpaper', + files: [ + 'sburbwp_1280x1024.jpg', + 'sburbwp_1440x900.jpg', + 'sburbwp_1920x1080.jpg', + ], + }, + { + title: 'Alternate Covers', + description: 'This is just an example description.', + files: [ + 'Homestuck_Vol4_alt1.jpg', + 'Homestuck_Vol4_alt2.jpg', + 'Homestuck_Vol4_alt3.jpg', + ], + }, + ], + ], + }); +}); diff --git a/test/snapshot/generateAlbumTrackListItem.js b/test/snapshot/generateAlbumTrackListItem.js new file mode 100644 index 00000000..e87c6de0 --- /dev/null +++ b/test/snapshot/generateAlbumTrackListItem.js @@ -0,0 +1,66 @@ +import t from 'tap'; +import {testContentFunctions} from '../lib/content-function.js'; + +testContentFunctions(t, 'generateAlbumTrackListItem (snapshot)', async (t, evaluate) => { + const artist1 = {directory: 'toby-fox', name: 'Toby Fox', urls: ['https://toby.fox/']}; + const artist2 = {directory: 'james-roach', name: 'James Roach'}; + const artist3 = {directory: 'clark-powell', name: 'Clark Powell'}; + const artist4 = {directory: ''} + const albumContribs = [{who: artist1}, {who: artist2}]; + + await evaluate.load(); + + evaluate.snapshot('basic behavior', { + name: 'generateAlbumTrackListItem', + args: [ + { + // Just pretend Hiveswap Act 1 OST doesn't have its own Artists field OK? + // We test that kind of case later! + name: 'Final Spice', + directory: 'final-spice', + duration: 54, + color: '#33cc77', + artistContribs: [ + {who: artist1, what: 'composition & arrangement'}, + {who: artist2, what: 'arrangement'}, + ], + }, + {artistContribs: []}, + ], + }); + + evaluate.snapshot('zero duration, zero artists', { + name: 'generateAlbumTrackListItem', + args: [ + { + name: 'You have got to be about the most superficial commentator on con-langues since the idiotic B. Gilson.', + directory: 'you-have-got-to-be-about-the-most-superficial-commentator-on-con-langues-since-the-idiotic-b-gilson', + duration: 0, + artistContribs: [], + }, + {artistContribs: []}, + ], + }); + + evaluate.snapshot('hide artists if inherited from album', { + name: 'generateAlbumTrackListItem', + multiple: [ + {args: [ + {directory: 'track1', name: 'Same artists, same order', artistContribs: [{who: artist1}, {who: artist2}]}, + {artistContribs: albumContribs}, + ]}, + {args: [ + {directory: 'track2', name: 'Same artists, different order', artistContribs: [{who: artist2}, {who: artist1}]}, + {artistContribs: albumContribs}, + ]}, + {args: [ + {directory: 'track3', name: 'Extra artist', artistContribs: [{who: artist1}, {who: artist2}, {who: artist3}]}, + {artistContribs: albumContribs}, + ]}, + {args: [ + {directory: 'track4', name: 'Missing artist', artistContribs: [{who: artist1}]}, + {artistContribs: albumContribs}, + ]}, + ], + }); +}); diff --git a/test/snapshot/generateContributionLinks.js b/test/snapshot/generateContributionLinks.js new file mode 100644 index 00000000..3283d3b2 --- /dev/null +++ b/test/snapshot/generateContributionLinks.js @@ -0,0 +1,55 @@ +// todo: this dependency was replaced with linkContribution, restructure test +// remove generateContributionLinks.js.test.cjs snapshot file too! + +import t from 'tap'; +import {testContentFunctions} from '../lib/content-function.js'; + +t.skip('generateContributionLinks (snapshot)'); + +void (() => testContentFunctions(t, 'generateContributionLinks (snapshot)', async (t, evaluate) => { + const artist1 = { + name: 'Clark Powell', + directory: 'clark-powell', + urls: ['https://soundcloud.com/plazmataz'], + }; + + const artist2 = { + name: 'Grounder & Scratch', + directory: 'the-big-baddies', + urls: [], + }; + + const artist3 = { + name: 'Toby Fox', + directory: 'toby-fox', + urls: ['https://tobyfox.bandcamp.com/', 'https://toby.fox/'], + }; + + const contributions = [ + {who: artist1, what: null}, + {who: artist2, what: 'Snooping'}, + {who: artist3, what: 'Arrangement'}, + ]; + + await evaluate.load(); + + evaluate.snapshot('showContribution & showIcons', { + name: 'generateContributionLinks', + args: [contributions, {showContribution: true, showIcons: true}], + }); + + evaluate.snapshot('only showContribution', { + name: 'generateContributionLinks', + args: [contributions, {showContribution: true, showIcons: false}], + }); + + evaluate.snapshot('only showIcons', { + name: 'generateContributionLinks', + args: [contributions, {showContribution: false, showIcons: true}], + }); + + evaluate.snapshot('no accents', { + name: 'generateContributionLinks', + args: [contributions, {showContribution: false, showIcons: false}], + }); +})); diff --git a/test/snapshot/image.js b/test/snapshot/image.js new file mode 100644 index 00000000..eeffb849 --- /dev/null +++ b/test/snapshot/image.js @@ -0,0 +1,92 @@ +import t from 'tap'; +import {testContentFunctions} from '../lib/content-function.js'; + +testContentFunctions(t, 'image (snapshot)', async (t, evaluate) => { + await evaluate.load(); + + const quickSnapshot = (message, opts) => + evaluate.snapshot(message, { + name: 'image', + extraDependencies: { + getSizeOfImageFile: () => 0, + }, + ...opts, + }); + + quickSnapshot('source via path', { + postprocess: template => template + .slot('path', ['media.albumCover', 'beyond-canon', 'png']), + }); + + quickSnapshot('source via src', { + postprocess: template => template + .slot('src', 'https://example.com/bananas.gif'), + }); + + quickSnapshot('source missing', { + postprocess: template => template + .slot('missingSourceContent', 'Example of missing source message.'), + }); + + quickSnapshot('id without link', { + postprocess: template => template + .slot('src', 'foobar') + .slot('id', 'banana'), + }); + + quickSnapshot('id with link', { + postprocess: template => template + .slot('src', 'foobar') + .slot('link', true) + .slot('id', 'banana'), + }); + + quickSnapshot('id with square', { + postprocess: template => template + .slot('src', 'foobar') + .slot('square', true) + .slot('id', 'banana'), + }) + + quickSnapshot('width & height', { + postprocess: template => template + .slot('src', 'foobar') + .slot('width', 600) + .slot('height', 400), + }); + + quickSnapshot('square', { + postprocess: template => template + .slot('src', 'foobar') + .slot('square', true), + }); + + quickSnapshot('lazy with square', { + postprocess: template => template + .slot('src', 'foobar') + .slot('lazy', true) + .slot('square', true), + }); + + quickSnapshot('link with file size', { + extraDependencies: { + getSizeOfImageFile: () => 10 ** 6, + }, + + postprocess: template => template + .slot('path', ['media.albumCover', 'pingas', 'png']) + .slot('link', true), + }); + + quickSnapshot('content warnings via tags', { + args: [ + [ + {name: 'Dirk Strider', directory: 'dirk'}, + {name: 'too cool for school', isContentWarning: true}, + ], + ], + + postprocess: template => template + .slot('path', ['media.albumCover', 'beyond-canon', 'png']), + }) +}); diff --git a/test/snapshot/linkArtist.js b/test/snapshot/linkArtist.js new file mode 100644 index 00000000..0451f1c5 --- /dev/null +++ b/test/snapshot/linkArtist.js @@ -0,0 +1,30 @@ +import t from 'tap'; +import {testContentFunctions} from '../lib/content-function.js'; + +testContentFunctions(t, 'linkArtist (snapshot)', async (t, evaluate) => { + await evaluate.load(); + + evaluate.snapshot('basic behavior', { + name: 'linkArtist', + args: [ + { + name: `Toby Fox`, + directory: `toby-fox`, + } + ], + }); + + evaluate.snapshot('prefer short name', { + name: 'linkArtist', + args: [ + { + name: 'ICCTTCMDMIROTMCWMWFTPFTDDOTARHPOESWGBTWEATFCWSEBTSSFOFG', + nameShort: '55gore', + directory: '55gore', + }, + ], + + postprocess: v => v + .slot('preferShortName', true), + }); +}); diff --git a/test/snapshot/linkExternal.js b/test/snapshot/linkExternal.js new file mode 100644 index 00000000..9661aead --- /dev/null +++ b/test/snapshot/linkExternal.js @@ -0,0 +1,57 @@ +import t from 'tap'; +import {testContentFunctions} from '../lib/content-function.js'; + +testContentFunctions(t, 'linkExternal (snapshot)', async (t, evaluate) => { + await evaluate.load(); + + evaluate.snapshot('missing domain (arbitrary local path)', { + name: 'linkExternal', + args: ['/foo/bar/baz.mp3'] + }); + + evaluate.snapshot('unknown domain (arbitrary world wide web path)', { + name: 'linkExternal', + args: ['https://snoo.ping.as/usual/i/see/'], + }); + + evaluate.snapshot('basic domain matches', { + name: 'linkExternal', + multiple: [ + {args: ['https://homestuck.bandcamp.com/']}, + {args: ['https://soundcloud.com/plazmataz']}, + {args: ['https://aeritus.tumblr.com/']}, + {args: ['https://twitter.com/awkwarddoesart']}, + {args: ['https://www.deviantart.com/chesswanderlust-sama']}, + {args: ['https://en.wikipedia.org/wiki/Haydn_Quartet_(vocal_ensemble)']}, + {args: ['https://www.poetryfoundation.org/poets/christina-rossetti']}, + {args: ['https://www.instagram.com/levc_egm/']}, + {args: ['https://www.patreon.com/CecilyRenns']}, + {args: ['https://open.spotify.com/artist/63SNNpNOicDzG3LY82G4q3']}, + {args: ['https://buzinkai.newgrounds.com/']}, + ], + }); + + evaluate.snapshot('custom matches - type: album', { + name: 'linkExternal', + multiple: [ + {args: ['https://youtu.be/abc', {type: 'album'}]}, + {args: ['https://youtube.com/watch?v=abc', {type: 'album'}]}, + {args: ['https://youtube.com/Playlist?list=kweh', {type: 'album'}]}, + + // Reuse default when no type specified + {args: ['https://youtu.be/abc']}, + {args: ['https://youtu.be/abc?list=kweh']}, + {args: ['https://youtube.com/watch?v=abc']}, + {args: ['https://youtube.com/watch?v=abc&list=kweh']}, + ], + }); + + evaluate.snapshot('custom domains for common platforms', { + name: 'linkExternal', + multiple: [ + // Just one domain of each platform is OK here + {args: ['https://music.solatrus.com/']}, + {args: ['https://types.pl/']}, + ], + }); +}); diff --git a/test/snapshot/linkExternalFlash.js b/test/snapshot/linkExternalFlash.js new file mode 100644 index 00000000..7bb86c6a --- /dev/null +++ b/test/snapshot/linkExternalFlash.js @@ -0,0 +1,24 @@ +import t from 'tap'; +import {testContentFunctions} from '../lib/content-function.js'; + +testContentFunctions(t, 'linkExternalFlash (snapshot)', async (t, evaluate) => { + await evaluate.load(); + + evaluate.snapshot('basic behavior', { + name: 'linkExternalFlash', + multiple: [ + {args: ['https://homestuck.com/story/4109/', {page: '4109'}]}, + {args: ['https://youtu.be/FDt-SLyEcjI', {page: '4109'}]}, + {args: ['https://www.bgreco.net/hsflash/006009.html', {page: '4109'}]}, + {args: ['https://www.newgrounds.com/portal/view/582345', {page: '4109'}]}, + ], + }); + + evaluate.snapshot('secret page', { + name: 'linkExternalFlash', + multiple: [ + {args: ['https://homestuck.com/story/pony/', {page: 'pony'}]}, + {args: ['https://youtu.be/USB1pj6hAjU', {page: 'pony'}]}, + ], + }); +}); diff --git a/test/snapshot/linkTemplate.js b/test/snapshot/linkTemplate.js new file mode 100644 index 00000000..e9a1ccd6 --- /dev/null +++ b/test/snapshot/linkTemplate.js @@ -0,0 +1,33 @@ +import t from 'tap'; +import {testContentFunctions} from '../lib/content-function.js'; + +testContentFunctions(t, 'linkTemplate (snapshot)', async (t, evaluate) => { + await evaluate.load(); + + evaluate.snapshot('fill many slots', { + name: 'linkTemplate', + + extraDependencies: { + getColors: c => ({primary: c + 'ff', dim: c + '77'}), + }, + + postprocess: v => v + .slot('color', '#123456') + .slot('href', 'https://hsmusic.wiki/media/cool file.pdf') + .slot('hash', 'fooey') + .slot('attributes', {class: 'dog', id: 'cat1'}) + .slot('content', 'My Cool Link'), + }); + + evaluate.snapshot('fill path slot & provide appendIndexHTML', { + name: 'linkTemplate', + + extraDependencies: { + to: (...path) => '/c*lzone/' + path.join('/') + '/', + appendIndexHTML: true, + }, + + postprocess: v => v + .slot('path', ['myCoolPath', 'ham', 'pineapple', 'tomato']), + }); +}); diff --git a/test/unit/content/dependencies/generateContributionLinks.js b/test/unit/content/dependencies/generateContributionLinks.js new file mode 100644 index 00000000..328adc0b --- /dev/null +++ b/test/unit/content/dependencies/generateContributionLinks.js @@ -0,0 +1,109 @@ +// todo: this dependency was replaced with linkContribution, restructure test + +import t from 'tap'; +import {testContentFunctions} from '../../../lib/content-function.js'; + +t.skip('generateContributionLinks (unit)', async t => { + const artist1 = { + name: 'Clark Powell', + urls: ['https://soundcloud.com/plazmataz'], + }; + + const artist2 = { + name: 'Grounder & Scratch', + urls: [], + }; + + const artist3 = { + name: 'Toby Fox', + urls: ['https://tobyfox.bandcamp.com/', 'https://toby.fox/'], + }; + + const contributions = [ + {who: artist1, what: null}, + {who: artist2, what: 'Snooping'}, + {who: artist3, what: 'Arrangement'}, + ]; + + await testContentFunctions(t, 'generateContributionLinks (unit 1)', async (t, evaluate) => { + const config = { + showContribution: true, + showIcons: true, + }; + + await evaluate.load({ + mock: evaluate.mock(mock => ({ + linkArtist: { + relations: mock.function('linkArtist.relations', () => ({})) + .args([undefined, artist1]).next() + .args([undefined, artist2]).next() + .args([undefined, artist3]), + + data: mock.function('linkArtist.data', () => ({})) + .args([artist1]).next() + .args([artist2]).next() + .args([artist3]), + + // This can be tweaked to return a specific (mocked) template + // for each artist if we need to test for slots in the future. + generate: mock.function('linkArtist.generate', () => 'artist link') + .repeat(3), + }, + + linkExternalAsIcon: { + data: mock.function('linkExternalAsIcon.data', () => ({})) + .args([artist1.urls[0]]).next() + .args([artist3.urls[0]]).next() + .args([artist3.urls[1]]), + + generate: mock.function('linkExternalAsIcon.generate', () => 'icon') + .repeat(3), + } + })), + }); + + evaluate({ + name: 'generateContributionLinks', + args: [contributions, config], + }); + }); + + await testContentFunctions(t, 'generateContributionLinks (unit 2)', async (t, evaluate) => { + const config = { + showContribution: false, + showIcons: false, + }; + + await evaluate.load({ + mock: evaluate.mock(mock => ({ + linkArtist: { + relations: mock.function('linkArtist.relations', () => ({})) + .args([undefined, artist1]).next() + .args([undefined, artist2]).next() + .args([undefined, artist3]), + + data: mock.function('linkArtist.data', () => ({})) + .args([artist1]).next() + .args([artist2]).next() + .args([artist3]), + + generate: mock.function(() => 'artist link') + .repeat(3), + }, + + linkExternalAsIcon: { + data: mock.function('linkExternalAsIcon.data', () => ({})) + .neverCalled(), + + generate: mock.function('linkExternalAsIcon.generate', () => 'icon') + .neverCalled(), + }, + })), + }); + + evaluate({ + name: 'generateContributionLinks', + args: [contributions, config], + }); + }); +}); diff --git a/test/unit/content/dependencies/linkArtist.js b/test/unit/content/dependencies/linkArtist.js new file mode 100644 index 00000000..9fceb97d --- /dev/null +++ b/test/unit/content/dependencies/linkArtist.js @@ -0,0 +1,31 @@ +import t from 'tap'; +import {testContentFunctions} from '../../../lib/content-function.js'; + +testContentFunctions(t, 'linkArtist (unit)', async (t, evaluate) => { + const artistObject = {}; + const linkTemplate = {}; + + await evaluate.load({ + mock: evaluate.mock(mock => ({ + linkThing: { + relations: mock.function('linkThing.relations', () => ({})) + .args([undefined, 'localized.artist', artistObject]) + .once(), + + data: mock.function('linkThing.data', () => ({})) + .args(['localized.artist', artistObject]) + .once(), + + generate: mock.function('linkThing.data', () => linkTemplate) + .once(), + } + })), + }); + + const result = evaluate({ + name: 'linkArtist', + args: [artistObject], + }); + + t.equal(result, linkTemplate); +}); diff --git a/test/cacheable-object.js b/test/unit/data/things/cacheable-object.js index 664a648b..d7a88ce7 100644 --- a/test/cacheable-object.js +++ b/test/unit/data/things/cacheable-object.js @@ -1,8 +1,6 @@ -import test from 'tape'; +import t from 'tap'; -import CacheableObject from '../src/data/cacheable-object.js'; - -// Utility +import CacheableObject from '../../../../src/data/things/cacheable-object.js'; function newCacheableObject(PD) { return new (class extends CacheableObject { @@ -10,9 +8,7 @@ function newCacheableObject(PD) { }); } -// Tests - -test(`CacheableObject simple separate update & expose`, t => { +t.test(`CacheableObject simple separate update & expose`, t => { const obj = newCacheableObject({ number: { flags: { @@ -37,7 +33,7 @@ test(`CacheableObject simple separate update & expose`, t => { t.equal(obj.timesTwo, 10); }); -test(`CacheableObject basic cache behavior`, t => { +t.test(`CacheableObject basic cache behavior`, t => { let computeCount = 0; const obj = newCacheableObject({ @@ -64,31 +60,31 @@ test(`CacheableObject basic cache behavior`, t => { t.plan(8); - t.is(computeCount, 0); + t.equal(computeCount, 0); obj.string = 'hello world'; - t.is(computeCount, 0); + t.equal(computeCount, 0); obj.karkat; - t.is(computeCount, 1); + t.equal(computeCount, 1); obj.karkat; - t.is(computeCount, 1); + t.equal(computeCount, 1); obj.string = 'testing once again'; - t.is(computeCount, 1); + t.equal(computeCount, 1); obj.karkat; - t.is(computeCount, 2); + t.equal(computeCount, 2); obj.string = 'testing once again'; - t.is(computeCount, 2); + t.equal(computeCount, 2); obj.karkat; - t.is(computeCount, 2); + t.equal(computeCount, 2); }); -test(`CacheableObject combined update & expose (no transform)`, t => { +t.test(`CacheableObject combined update & expose (no transform)`, t => { const obj = newCacheableObject({ directory: { flags: { @@ -100,14 +96,14 @@ test(`CacheableObject combined update & expose (no transform)`, t => { t.plan(2); - t.directory = 'the-world-revolving'; - t.is(t.directory, 'the-world-revolving'); + obj.directory = 'the-world-revolving'; + t.equal(obj.directory, 'the-world-revolving'); - t.directory = 'chaos-king'; - t.is(t.directory, 'chaos-king'); + obj.directory = 'chaos-king'; + t.equal(obj.directory, 'chaos-king'); }); -test(`CacheableObject combined update & expose (basic transform)`, t => { +t.test(`CacheableObject combined update & expose (basic transform)`, t => { const obj = newCacheableObject({ getsRepeated: { flags: { @@ -124,10 +120,10 @@ test(`CacheableObject combined update & expose (basic transform)`, t => { t.plan(1); obj.getsRepeated = 'dog'; - t.is(obj.getsRepeated, 'dogdog'); + t.equal(obj.getsRepeated, 'dogdog'); }); -test(`CacheableObject combined update & expose (transform with dependency)`, t => { +t.test(`CacheableObject combined update & expose (transform with dependency)`, t => { const obj = newCacheableObject({ customRepeat: { flags: { @@ -152,16 +148,16 @@ test(`CacheableObject combined update & expose (transform with dependency)`, t = obj.customRepeat = 'dog'; obj.times = 1; - t.is(obj.customRepeat, 'dog'); + t.equal(obj.customRepeat, 'dog'); obj.times = 5; - t.is(obj.customRepeat, 'dogdogdogdogdog'); + t.equal(obj.customRepeat, 'dogdogdogdogdog'); obj.customRepeat = 'cat'; - t.is(obj.customRepeat, 'catcatcatcatcat'); + t.equal(obj.customRepeat, 'catcatcatcatcat'); }); -test(`CacheableObject validate on update`, t => { +t.test(`CacheableObject validate on update`, t => { const mockError = new TypeError(`Expected a string, not ${typeof value}`); const obj = newCacheableObject({ @@ -197,7 +193,7 @@ test(`CacheableObject validate on update`, t => { t.plan(6); obj.directory = 'megalovania'; - t.is(obj.directory, 'megalovania'); + t.equal(obj.directory, 'megalovania'); try { obj.directory = 25; @@ -205,13 +201,13 @@ test(`CacheableObject validate on update`, t => { thrownError = err; } - t.is(thrownError, mockError); - t.is(obj.directory, 'megalovania'); + t.equal(thrownError, mockError); + t.equal(obj.directory, 'megalovania'); const date = new Date(`25 December 2009`); obj.date = date; - t.is(obj.date, date); + t.equal(obj.date, date); try { obj.date = `TWELFTH PERIGEE'S EVE`; @@ -219,11 +215,11 @@ test(`CacheableObject validate on update`, t => { thrownError = err; } - t.is(thrownError?.constructor, TypeError); - t.is(obj.date, date); + t.equal(thrownError?.constructor, TypeError); + t.equal(obj.date, date); }); -test(`CacheableObject default update property value`, t => { +t.test(`CacheableObject default update property value`, t => { const obj = newCacheableObject({ fruit: { flags: { @@ -238,10 +234,10 @@ test(`CacheableObject default update property value`, t => { }); t.plan(1); - t.is(obj.fruit, 'potassium'); + t.equal(obj.fruit, 'potassium'); }); -test(`CacheableObject default property throws if invalid`, t => { +t.test(`CacheableObject default property throws if invalid`, t => { const mockError = new TypeError(`Expected a string, not ${typeof value}`); t.plan(1); @@ -270,5 +266,5 @@ test(`CacheableObject default property throws if invalid`, t => { thrownError = err; } - t.is(thrownError, mockError); + t.equal(thrownError, mockError); }); diff --git a/test/things.js b/test/unit/data/things/track.js index 0d74b60d..0dad0e62 100644 --- a/test/things.js +++ b/test/unit/data/things/track.js @@ -1,11 +1,13 @@ -import test from 'tape'; +import t from 'tap'; -import { +import thingConstructors from '../../../../src/data/things/index.js'; + +const { Album, Thing, Track, TrackGroup, -} from '../src/data/things.js'; +} = thingConstructors; function stubAlbum(tracks) { const album = new Album(); @@ -18,7 +20,7 @@ function stubAlbum(tracks) { return album; } -test(`Track.coverArtDate`, t => { +t.test(`Track.coverArtDate`, t => { t.plan(5); // Priority order is as follows, with the last (trackCoverArtDate) being @@ -37,7 +39,7 @@ test(`Track.coverArtDate`, t => { // 1. coverArtDate defaults to null - t.is(track.coverArtDate, null); + t.equal(track.coverArtDate, null); // 2. coverArtDate inherits album release date @@ -47,7 +49,7 @@ test(`Track.coverArtDate`, t => { track.albumData = []; track.albumData = [album]; - t.is(track.coverArtDate, albumDate); + t.equal(track.coverArtDate, albumDate); // 3. coverArtDate inherits album trackArtDate @@ -57,17 +59,17 @@ test(`Track.coverArtDate`, t => { track.albumData = []; track.albumData = [album]; - t.is(track.coverArtDate, albumTrackArtDate); + t.equal(track.coverArtDate, albumTrackArtDate); // 4. coverArtDate is overridden dateFirstReleased track.dateFirstReleased = trackDateFirstReleased; - t.is(track.coverArtDate, trackDateFirstReleased); + t.equal(track.coverArtDate, trackDateFirstReleased); // 5. coverArtDate is overridden coverArtDate track.coverArtDate = trackCoverArtDate; - t.is(track.coverArtDate, trackCoverArtDate); + t.equal(track.coverArtDate, trackCoverArtDate); }); diff --git a/test/data-validators.js b/test/unit/data/things/validators.js index f13f3f0f..53cba063 100644 --- a/test/data-validators.js +++ b/test/unit/data/things/validators.js @@ -1,10 +1,11 @@ -import _test from 'tape'; -import { showAggregate } from '../src/util/sugar.js'; +import t from 'tap'; +import { showAggregate } from '../../../../src/util/sugar.js'; import { // Basic types isBoolean, isCountingNumber, + isDate, isNumber, isString, isStringNonEmpty, @@ -15,19 +16,25 @@ import { validateArrayItems, // Wiki data + isColor, + isCommentary, + isContribution, + isContributionList, isDimensions, isDirectory, isDuration, isFileExtension, + isName, + isURL, validateReference, validateReferenceList, // Compositional utilities oneOf, -} from '../src/data/validators.js'; +} from '../../../../src/data/things/validators.js'; -function test(msg, fn) { - _test(msg, t => { +function test(t, msg, fn) { + t.test(msg, t => { try { fn(t); } catch (error) { @@ -39,11 +46,9 @@ function test(msg, fn) { }); } -test.skip = _test.skip; - // Basic types -test('isBoolean', t => { +test(t, 'isBoolean', t => { t.plan(4); t.ok(isBoolean(true)); t.ok(isBoolean(false)); @@ -51,7 +56,7 @@ test('isBoolean', t => { t.throws(() => isBoolean('yes'), TypeError); }); -test('isNumber', t => { +test(t, 'isNumber', t => { t.plan(6); t.ok(isNumber(123)); t.ok(isNumber(0.05)); @@ -61,7 +66,7 @@ test('isNumber', t => { t.throws(() => isNumber(true), TypeError); }); -test('isCountingNumber', t => { +test(t, 'isCountingNumber', t => { t.plan(6); t.ok(isCountingNumber(3)); t.ok(isCountingNumber(1)); @@ -71,14 +76,14 @@ test('isCountingNumber', t => { t.throws(() => isCountingNumber('612'), TypeError); }); -test('isString', t => { +test(t, 'isString', t => { t.plan(3); t.ok(isString('hello!')); t.ok(isString('')); t.throws(() => isString(100), TypeError); }); -test('isStringNonEmpty', t => { +test(t, 'isStringNonEmpty', t => { t.plan(4); t.ok(isStringNonEmpty('hello!')); t.throws(() => isStringNonEmpty(''), TypeError); @@ -88,25 +93,28 @@ test('isStringNonEmpty', t => { // Complex types -test('isArray', t => { +test(t, 'isArray', t => { t.plan(3); t.ok(isArray([])); t.throws(() => isArray({}), TypeError); t.throws(() => isArray('1, 2, 3'), TypeError); }); -test.skip('isDate', t => { - // TODO +test(t, 'isDate', t => { + t.plan(3); + t.ok(isDate(new Date('2023-03-27 09:24:15'))); + t.throws(() => isDate(new Date(Infinity)), TypeError); + t.throws(() => isDimensions('2023-03-27 09:24:15'), TypeError); }); -test('isObject', t => { +test(t, 'isObject', t => { t.plan(3); t.ok(isObject({})); t.ok(isObject([])); t.throws(() => isObject(null), TypeError); }); -test('validateArrayItems', t => { +test(t, 'validateArrayItems', t => { t.plan(6); t.ok(validateArrayItems(isNumber)([3, 4, 5])); @@ -119,31 +127,56 @@ test('validateArrayItems', t => { caughtError = err; } - t.isNot(caughtError, null); - t.true(caughtError instanceof AggregateError); - t.is(caughtError.errors.length, 1); - t.true(caughtError.errors[0] instanceof TypeError); + t.not(caughtError, null); + t.ok(caughtError instanceof AggregateError); + t.equal(caughtError.errors.length, 1); + t.ok(caughtError.errors[0] instanceof TypeError); }); // Wiki data -test.skip('isColor', t => { - // TODO +t.test('isColor', t => { + t.plan(9); + t.ok(isColor('#123')); + t.ok(isColor('#1234')); + t.ok(isColor('#112233')); + t.ok(isColor('#11223344')); + t.ok(isColor('#abcdef00')); + t.ok(isColor('#ABCDEF')); + t.throws(() => isColor('#ggg'), TypeError); + t.throws(() => isColor('red'), TypeError); + t.throws(() => isColor('hsl(150deg 30% 60%)'), TypeError); }); -test.skip('isCommentary', t => { - // TODO +t.test('isCommentary', t => { + t.plan(6); + t.ok(isCommentary(`<i>Toby Fox:</i>\ndogsong.mp3`)); + t.ok(isCommentary(`Technically, this works:</i>`)); + t.ok(isCommentary(`<i><b>Whodunnit:</b></i>`)); + t.throws(() => isCommentary(123), TypeError); + t.throws(() => isCommentary(``), TypeError); + t.throws(() => isCommentary(`<i><u>Toby Fox:</u></i>`)); }); -test.skip('isContribution', t => { - // TODO +t.test('isContribution', t => { + t.plan(4); + t.ok(isContribution({who: 'artist:toby-fox', what: 'Music'})); + t.ok(isContribution({who: 'Toby Fox'})); + t.throws(() => isContribution(({who: 'group:umspaf', what: 'Organizing'})), + {errors: /who/}); + t.throws(() => isContribution(({who: 'artist:toby-fox', what: 123})), + {errors: /what/}); }); -test.skip('isContributionList', t => { - // TODO +t.test('isContributionList', t => { + t.plan(4); + t.ok(isContributionList([{who: 'Beavis'}, {who: 'Butthead', what: 'Wrangling'}])); + t.ok(isContributionList([])); + t.throws(() => isContributionList(2)); + t.throws(() => isContributionList(['Charlie', 'Woodstock'])); }); -test('isDimensions', t => { +test(t, 'isDimensions', t => { t.plan(6); t.ok(isDimensions([1, 1])); t.ok(isDimensions([50, 50])); @@ -153,7 +186,7 @@ test('isDimensions', t => { t.throws(() => isDimensions('800x200'), TypeError); }); -test('isDirectory', t => { +test(t, 'isDirectory', t => { t.plan(6); t.ok(isDirectory('savior-of-the-waking-world')); t.ok(isDirectory('MeGaLoVania')); @@ -163,7 +196,7 @@ test('isDirectory', t => { t.throws(() => isDirectory('troll saint nicholas and the quest for the holy pail'), TypeError); }); -test('isDuration', t => { +test(t, 'isDuration', t => { t.plan(5); t.ok(isDuration(60)); t.ok(isDuration(0.02)); @@ -172,7 +205,7 @@ test('isDuration', t => { t.throws(() => isDuration('10:25'), TypeError); }); -test('isFileExtension', t => { +test(t, 'isFileExtension', t => { t.plan(6); t.ok(isFileExtension('png')); t.ok(isFileExtension('jpg')); @@ -182,15 +215,23 @@ test('isFileExtension', t => { t.throws(() => isFileExtension('just an image bro!!!!'), TypeError); }); -test.skip('isName', t => { - // TODO +t.test('isName', t => { + t.plan(4); + t.ok(isName('Dogz 2.0')); + t.ok(isName('album:this-track-is-only-named-thusly-to-give-niklink-a-headache')); + t.ok(isName('')); + t.throws(() => isName(612)); }); -test.skip('isURL', t => { - // TODO +t.test('isURL', t => { + t.plan(4); + t.ok(isURL(`https://hsmusic.wiki/foo/bar/hi?baz=25#hash`)); + t.throws(() => isURL(`/the/dog/zone/`)); + t.throws(() => isURL(25)); + t.throws(() => isURL(new URL(`https://hsmusic.wiki/perfectly/reasonable/`))); }); -test('validateReference', t => { +test(t, 'validateReference', t => { t.plan(16); const typeless = validateReference(); @@ -217,7 +258,7 @@ test('validateReference', t => { t.throws(() => typeless('album:undertale-soundtrack')); }); -test('validateReferenceList', t => { +test(t, 'validateReferenceList', t => { const track = validateReferenceList('track'); const artist = validateReferenceList('artist'); @@ -235,14 +276,14 @@ test('validateReferenceList', t => { caughtError = err; } - t.isNot(caughtError, null); - t.true(caughtError instanceof AggregateError); - t.is(caughtError.errors.length, 2); - t.true(caughtError.errors[0] instanceof TypeError); - t.true(caughtError.errors[1] instanceof TypeError); + t.not(caughtError, null); + t.ok(caughtError instanceof AggregateError); + t.equal(caughtError.errors.length, 2); + t.ok(caughtError.errors[0] instanceof TypeError); + t.ok(caughtError.errors[1] instanceof TypeError); }); -test('oneOf', t => { +test(t, 'oneOf', t => { t.plan(11); const isStringOrNumber = oneOf(isString, isNumber); @@ -267,11 +308,11 @@ test('oneOf', t => { caughtError = err; } - t.isNot(caughtError, null); - t.true(caughtError instanceof AggregateError); - t.is(caughtError.errors.length, 2); - t.true(caughtError.errors[0] instanceof TypeError); - t.is(caughtError.errors[0].check, isString); - t.is(caughtError.errors[1], mockError); - t.is(caughtError.errors[1].check, neverSucceeds); + t.not(caughtError, null); + t.ok(caughtError instanceof AggregateError); + t.equal(caughtError.errors.length, 2); + t.ok(caughtError.errors[0] instanceof TypeError); + t.equal(caughtError.errors[0].check, isString); + t.equal(caughtError.errors[1], mockError); + t.equal(caughtError.errors[1].check, neverSucceeds); }); diff --git a/test/unit/util/html.js b/test/unit/util/html.js new file mode 100644 index 00000000..82f96b48 --- /dev/null +++ b/test/unit/util/html.js @@ -0,0 +1,906 @@ +import t from 'tap'; + +import * as html from '../../../src/util/html.js'; +const {Tag, Attributes, Template} = html; + +import {strictlyThrows} from '../../lib/strict-match-error.js'; + +t.test(`html.tag`, t => { + t.plan(14); + + const tag1 = + html.tag('div', + {[html.onlyIfContent]: true, foo: 'bar'}, + 'child'); + + // 1-5: basic behavior when passing attributes + t.ok(tag1 instanceof Tag); + t.ok(tag1.onlyIfContent); + t.equal(tag1.attributes.get('foo'), 'bar'); + t.equal(tag1.content.length, 1); + t.equal(tag1.content[0], 'child'); + + const tag2 = html.tag('div', ['two', 'children']); + + // 6-8: basic behavior when not passing attributes + t.equal(tag2.content.length, 2); + t.equal(tag2.content[0], 'two'); + t.equal(tag2.content[1], 'children'); + + const genericTag = html.tag('div'); + const genericTemplate = html.template({ + content: () => html.blank(), + }); + + // 9-10: tag treated as content, not attributes + const tag3 = html.tag('div', genericTag); + t.equal(tag3.content.length, 1); + t.equal(tag3.content[0], genericTag); + + // 11-12: template treated as content, not attributes + const tag4 = html.tag('div', genericTemplate); + t.equal(tag4.content.length, 1); + t.equal(tag4.content[0], genericTemplate); + + // 13-14: deep flattening support + const tag6 = + html.tag('div', [ + true && + [[[[[[ + true && + [[[[[`That's deep.`]]]]], + ]]]]]], + ]); + t.equal(tag6.content.length, 1); + t.equal(tag6.content[0], `That's deep.`); +}); + +t.test(`Tag (basic interface)`, t => { + t.plan(11); + + const tag1 = new Tag(); + + // 1-5: essential properties & no arguments provided + t.equal(tag1.tagName, ''); + t.ok(Array.isArray(tag1.content)); + t.equal(tag1.content.length, 0); + t.ok(tag1.attributes instanceof Attributes); + t.equal(tag1.attributes.toString(), ''); + + const tag2 = new Tag('div', {id: 'banana'}, ['one', 'two', tag1]); + + // 6-11: properties on basic usage + t.equal(tag2.tagName, 'div'); + t.equal(tag2.content.length, 3); + t.equal(tag2.content[0], 'one'); + t.equal(tag2.content[1], 'two'); + t.equal(tag2.content[2], tag1); + t.equal(tag2.attributes.get('id'), 'banana'); +}); + +t.test(`Tag (self-closing)`, t => { + t.plan(10); + + const tag1 = new Tag('br'); + const tag2 = new Tag('div'); + const tag3 = new Tag('div'); + tag3.tagName = 'br'; + + // 1-3: selfClosing depends on tagName + t.ok(tag1.selfClosing); + t.notOk(tag2.selfClosing); + t.ok(tag3.selfClosing); + + // 4: constructing self-closing tag with content throws + t.throws(() => new Tag('br', null, 'bananas'), /self-closing/); + + // 5: setting content on self-closing tag throws + t.throws(() => { tag1.content = ['suspicious']; }, /self-closing/); + + // 6-9: setting empty content on self-closing tag doesn't throw + t.doesNotThrow(() => { tag1.content = null; }); + t.doesNotThrow(() => { tag1.content = undefined; }); + t.doesNotThrow(() => { tag1.content = ''; }); + t.doesNotThrow(() => { tag1.content = [null, '', false]; }); + + const tag4 = new Tag('div', null, 'bananas'); + + // 10: changing tagName to self-closing when tag has content throws + t.throws(() => { tag4.tagName = 'br'; }, /self-closing/); +}); + +t.test(`Tag (properties from attributes - from constructor)`, t => { + t.plan(6); + + const tag = new Tag('div', { + [html.onlyIfContent]: true, + [html.noEdgeWhitespace]: true, + [html.joinChildren]: '<br>', + }); + + // 1-3: basic exposed properties from attributes in constructor + t.ok(tag.onlyIfContent); + t.ok(tag.noEdgeWhitespace); + t.equal(tag.joinChildren, '<br>'); + + // 4-6: property values stored on attributes with public symbols + t.equal(tag.attributes.get(html.onlyIfContent), true); + t.equal(tag.attributes.get(html.noEdgeWhitespace), true); + t.equal(tag.attributes.get(html.joinChildren), '<br>'); +}); + +t.test(`Tag (properties from attributes - mutating)`, t => { + t.plan(12); + + // 1-3: exposed properties reflect reasonable attribute values + + const tag1 = new Tag('div', { + [html.onlyIfContent]: true, + [html.noEdgeWhitespace]: true, + [html.joinChildren]: '<br>', + }); + + tag1.attributes.set(html.onlyIfContent, false); + tag1.attributes.remove(html.noEdgeWhitespace); + tag1.attributes.set(html.joinChildren, '🍇'); + + t.equal(tag1.onlyIfContent, false); + t.equal(tag1.noEdgeWhitespace, false); + t.equal(tag1.joinChildren, '🍇'); + + // 4-6: exposed properties reflect unreasonable attribute values + + const tag2 = new Tag('div', { + [html.onlyIfContent]: true, + [html.noEdgeWhitespace]: true, + [html.joinChildren]: '<br>', + }); + + tag2.attributes.set(html.onlyIfContent, ''); + tag2.attributes.set(html.noEdgeWhitespace, 12345); + tag2.attributes.set(html.joinChildren, 0.0001); + + t.equal(tag2.onlyIfContent, false); + t.equal(tag2.noEdgeWhitespace, true); + t.equal(tag2.joinChildren, '0.0001'); + + // 7-9: attribute values reflect reasonable mutated properties + + const tag3 = new Tag('div', null, { + [html.onlyIfContent]: false, + [html.noEdgeWhitespace]: true, + [html.joinChildren]: '🍜', + }) + + tag3.onlyIfContent = true; + tag3.noEdgeWhitespace = false; + tag3.joinChildren = '🦑'; + + t.equal(tag3.attributes.get(html.onlyIfContent), true); + t.equal(tag3.attributes.get(html.noEdgeWhitespace), undefined); + t.equal(tag3.joinChildren, '🦑'); + + // 10-12: attribute values reflect unreasonable mutated properties + + const tag4 = new Tag('div', null, { + [html.onlyIfContent]: false, + [html.noEdgeWhitespace]: true, + [html.joinChildren]: '🍜', + }); + + tag4.onlyIfContent = 'armadillo'; + tag4.noEdgeWhitespace = 0; + tag4.joinChildren = Infinity; + + t.equal(tag4.attributes.get(html.onlyIfContent), true); + t.equal(tag4.attributes.get(html.noEdgeWhitespace), undefined); + t.equal(tag4.attributes.get(html.joinChildren), 'Infinity'); +}); + +t.test(`Tag.toString`, t => { + t.plan(9); + + // 1: basic behavior + + const tag1 = + html.tag('div', 'Content'); + + t.equal(tag1.toString(), + `<div>Content</div>`); + + // 2: stringifies nested element + + const tag2 = + html.tag('div', html.tag('p', 'Content')); + + t.equal(tag2.toString(), + `<div><p>Content</p></div>`); + + // 3: stringifies attributes + + const tag3 = + html.tag('div', + { + id: 'banana', + class: ['foo', 'bar'], + contenteditable: true, + biggerthanabreadbox: false, + saying: `"To light a candle is to cast a shadow..."`, + tabindex: 413, + }, + 'Content'); + + t.equal(tag3.toString(), + `<div id="banana" class="foo bar" contenteditable ` + + `saying=""To light a candle is to cast a shadow..."" ` + + `tabindex="413">Content</div>`); + + // 4: attributes match input order + + const tag4 = + html.tag('div', + {class: ['foo', 'bar'], id: 'banana'}, + 'Content'); + + t.equal(tag4.toString(), + `<div class="foo bar" id="banana">Content</div>`); + + // 5: multiline contented indented + + const tag5 = + html.tag('div', 'foo\nbar'); + + t.equal(tag5.toString(), + `<div>\n` + + ` foo\n` + + ` bar\n` + + `</div>`); + + // 6: nested multiline content double-indented + + const tag6 = + html.tag('div', [ + html.tag('p', + 'foo\nbar'), + html.tag('span', `I'm on one line!`), + ]); + + t.equal(tag6.toString(), + `<div>\n` + + ` <p>\n` + + ` foo\n` + + ` bar\n` + + ` </p>\n` + + ` <span>I'm on one line!</span>\n` + + `</div>`); + + // 7: self-closing (with attributes) + + const tag7 = + html.tag('article', [ + html.tag('h1', `Title`), + html.tag('hr', {style: `color: magenta`}), + html.tag('p', `Shenanigans!`), + ]); + + t.equal(tag7.toString(), + `<article>\n` + + ` <h1>Title</h1>\n` + + ` <hr style="color: magenta">\n` + + ` <p>Shenanigans!</p>\n` + + `</article>`); + + // 8-9: empty tagName passes content through directly + + const tag8 = + html.tag(null, [ + html.tag('h1', `Foo`), + html.tag(`h2`, `Bar`), + ]); + + t.equal(tag8.toString(), + `<h1>Foo</h1>\n` + + `<h2>Bar</h2>`); + + const tag9 = + html.tag(null, { + [html.joinChildren]: html.tag('br'), + }, [ + `Say it with me...`, + `Supercalifragilisticexpialidocious!` + ]); + + t.equal(tag9.toString(), + `Say it with me...\n` + + `<br>\n` + + `Supercalifragilisticexpialidocious!`); +}); + +t.test(`Tag.toString (onlyIfContent)`, t => { + t.plan(4); + + // 1-2: basic behavior + + const tag1 = + html.tag('div', + {[html.onlyIfContent]: true}, + `Hello!`); + + t.equal(tag1.toString(), + `<div>Hello!</div>`); + + const tag2 = + html.tag('div', + {[html.onlyIfContent]: true}, + ''); + + t.equal(tag2.toString(), + ''); + + // 3-4: nested onlyIfContent with "more" content + + const tag3 = + html.tag('div', + {[html.onlyIfContent]: true}, + [ + '', + 0, + html.tag('h1', + {[html.onlyIfContent]: true}, + html.tag('strong', + {[html.onlyIfContent]: true})), + null, + false, + ]); + + t.equal(tag3.toString(), + ''); + + const tag4 = + html.tag('div', + {[html.onlyIfContent]: true}, + [ + '', + 0, + html.tag('h1', + {[html.onlyIfContent]: true}, + html.tag('strong')), + null, + false, + ]); + + t.equal(tag4.toString(), + `<div><h1><strong></strong></h1></div>`); +}); + +t.test(`Tag.toString (joinChildren, noEdgeWhitespace)`, t => { + t.plan(6); + + // 1: joinChildren: default (\n), noEdgeWhitespace: true + + const tag1 = + html.tag('div', + {[html.noEdgeWhitespace]: true}, + [ + 'Foo', + 'Bar', + 'Baz', + ]); + + t.equal(tag1.toString(), + `<div>Foo\n` + + ` Bar\n` + + ` Baz</div>`); + + // 2: joinChildren: one-line string, noEdgeWhitespace: default (false) + + const tag2 = + html.tag('div', + { + [html.joinChildren]: + html.tag('br', {location: '🍍'}), + }, + [ + 'Foo', + 'Bar', + 'Baz', + ]); + + t.equal(tag2.toString(), + `<div>\n` + + ` Foo\n` + + ` <br location="🍍">\n` + + ` Bar\n` + + ` <br location="🍍">\n` + + ` Baz\n` + + `</div>`); + + // 3-4: joinChildren: blank string, noEdgeWhitespace: default (false) + + const tag3 = + html.tag('div', + {[html.joinChildren]: ''}, + [ + 'Foo', + 'Bar', + 'Baz', + ]); + + t.equal(tag3.toString(), + `<div>FooBarBaz</div>`); + + const tag4 = + html.tag('div', + {[html.joinChildren]: ''}, + [ + `Ain't I\na cute one?`, + `~` + ]); + + t.equal(tag4.toString(), + `<div>\n` + + ` Ain't I\n` + + ` a cute one?~\n` + + `</div>`); + + // 5: joinChildren: one-line string, noEdgeWhitespace: true + + const tag5 = + html.tag('div', + { + [html.joinChildren]: html.tag('br'), + [html.noEdgeWhitespace]: true, + }, + [ + 'Foo', + 'Bar', + 'Baz', + ]); + + t.equal(tag5.toString(), + `<div>Foo\n` + + ` <br>\n` + + ` Bar\n` + + ` <br>\n` + + ` Baz</div>`); + + // 6: joinChildren: empty string, noEdgeWhitespace: true + + const tag6 = + html.tag('span', + { + [html.joinChildren]: '', + [html.noEdgeWhitespace]: true, + }, + [ + html.tag('i', `Oh yes~ `), + `You're a cute one`, + html.tag('sup', `💕`), + ]); + + t.equal(tag6.toString(), + `<span><i>Oh yes~ </i>You're a cute one<sup>💕</sup></span>`); + +}); + +t.test(`Tag.toString (custom attributes)`, t => { + t.plan(1); + + t.test(`Tag.toString (custom attribute: href)`, t => { + t.plan(2); + + const tag1 = html.tag('a', {href: `https://hsmusic.wiki/`}); + t.equal(tag1.toString(), `<a href="https://hsmusic.wiki/"></a>`); + + const tag2 = html.tag('a', {href: `https://hsmusic.wiki/media/Album Booklet.pdf`}); + t.equal(tag2.toString(), `<a href="https://hsmusic.wiki/media/Album%20Booklet.pdf"></a>`); + }); +}); + +t.test(`html.template`, t => { + t.plan(11); + + let contentCalls; + + // 1-4: basic behavior - no slots + + contentCalls = 0; + + const template1 = html.template({ + content() { + contentCalls++; + return html.tag('hr'); + }, + }); + + t.equal(contentCalls, 0); + t.equal(template1.toString(), `<hr>`); + t.equal(contentCalls, 1); + template1.toString(); + t.equal(contentCalls, 2); + + // 5-10: basic behavior - slots + + contentCalls = 0; + + const template2 = html.template({ + slots: { + foo: { + type: 'string', + default: 'Default Message', + }, + }, + + content(slots) { + contentCalls++; + return html.tag('sub', slots.foo.toLowerCase()); + }, + }); + + t.equal(contentCalls, 0); + t.equal(template2.toString(), `<sub>default message</sub>`); + t.equal(contentCalls, 1); + template2.setSlot('foo', `R-r-really, me?`); + t.equal(contentCalls, 1); + t.equal(template2.toString(), `<sub>r-r-really, me?</sub>`); + t.equal(contentCalls, 2); + + // 11: slot uses default only for null, not falsey + + const template3 = html.template({ + slots: { + slot1: {type: 'number', default: 123}, + slot2: {type: 'number', default: 456}, + slot3: {type: 'boolean', default: true}, + slot4: {type: 'string', default: 'banana'}, + }, + + content(slots) { + return html.tag('span', [ + slots.slot1, + slots.slot2, + slots.slot3, + `(length: ${slots.slot4.length})`, + ].join(' ')); + }, + }); + + template3.setSlots({ + slot1: null, + slot2: 0, + slot3: false, + slot4: '', + }); + + t.equal(template3.toString(), `<span>123 0 false (length: 0)</span>`); +}); + +t.test(`Template - description errors`, t => { + t.plan(14); + + // 1-3: top-level description is object + + strictlyThrows(t, + () => Template.validateDescription('snooping as usual'), + new TypeError(`Expected object, got string`)); + + strictlyThrows(t, + () => Template.validateDescription(), + new TypeError(`Expected object, got undefined`)); + + strictlyThrows(t, + () => Template.validateDescription(null), + new TypeError(`Expected object, got null`)); + + // 4-5: description.content is function + + strictlyThrows(t, + () => Template.validateDescription({}), + new AggregateError([ + new TypeError(`Expected description.content`), + ], `Errors validating template description`)); + + strictlyThrows(t, + () => Template.validateDescription({ + content: 'pingas', + }), + new AggregateError([ + new TypeError(`Expected description.content to be function`), + ], `Errors validating template description`)); + + // 6: aggregate error includes template annotation + + strictlyThrows(t, + () => Template.validateDescription({ + annotation: `my cool template`, + content: 'pingas', + }), + new AggregateError([ + new TypeError(`Expected description.content to be function`), + ], `Errors validating template "my cool template" description`)); + + // 7: description.slots is object + + strictlyThrows(t, + () => Template.validateDescription({ + slots: 'pingas', + content: () => {}, + }), + new AggregateError([ + new TypeError(`Expected description.slots to be object`), + ], `Errors validating template description`)); + + // 8: slot description is object + + strictlyThrows(t, + () => Template.validateDescription({ + slots: { + mySlot: 'pingas', + }, + + content: () => {}, + }), + new AggregateError([ + new AggregateError([ + new TypeError(`(mySlot) Expected slot description to be object`), + ], `Errors in slot descriptions`), + ], `Errors validating template description`)) + + // 9-10: slot description has validate or default, not both + + strictlyThrows(t, + () => Template.validateDescription({ + slots: { + mySlot: {}, + }, + content: () => {}, + }), + new AggregateError([ + new AggregateError([ + new TypeError(`(mySlot) Expected either slot validate or type`), + ], `Errors in slot descriptions`), + ], `Errors validating template description`)); + + strictlyThrows(t, + () => Template.validateDescription({ + slots: { + mySlot: { + validate: 'pingas', + type: 'pingas', + }, + }, + content: () => {}, + }), + new AggregateError([ + new AggregateError([ + new TypeError(`(mySlot) Don't specify both slot validate and type`), + ], `Errors in slot descriptions`), + ], `Errors validating template description`)); + + // 11: slot validate is function + + strictlyThrows(t, + () => Template.validateDescription({ + slots: { + mySlot: { + validate: 'pingas', + }, + }, + content: () => {}, + }), + new AggregateError([ + new AggregateError([ + new TypeError(`(mySlot) Expected slot validate to be function`), + ], `Errors in slot descriptions`), + ], `Errors validating template description`)); + + // 12: slot type is name of built-in type + + strictlyThrows(t, + () => Template.validateDescription({ + slots: { + mySlot: { + type: 'pingas', + }, + }, + content: () => {}, + }), + new AggregateError([ + new AggregateError([ + /\(mySlot\) Expected slot type to be one of/, + ], `Errors in slot descriptions`), + ], `Errors validating template description`)); + + // 13: slot type has specific errors for function & object + + strictlyThrows(t, + () => Template.validateDescription({ + slots: { + slot1: {type: 'function'}, + slot2: {type: 'object'}, + }, + content: () => {}, + }), + new AggregateError([ + new AggregateError([ + new TypeError(`(slot1) Functions shouldn't be provided to slots`), + new TypeError(`(slot2) Provide validate function instead of type: object`), + ], `Errors in slot descriptions`), + ], `Errors validating template description`)); + + // 14: all intended types are supported + + t.doesNotThrow( + () => Template.validateDescription({ + slots: { + slot1: {type: 'string'}, + slot2: {type: 'number'}, + slot3: {type: 'bigint'}, + slot4: {type: 'boolean'}, + slot5: {type: 'symbol'}, + slot6: {type: 'html'}, + }, + content: () => {}, + })); +}); + +t.test(`Template - slot value errors`, t => { + t.plan(8); + + const template1 = html.template({ + slots: { + basicString: {type: 'string'}, + basicNumber: {type: 'number'}, + basicBigint: {type: 'bigint'}, + basicBoolean: {type: 'boolean'}, + basicSymbol: {type: 'symbol'}, + basicHTML: {type: 'html'}, + }, + + content: slots => + html.tag('p', [ + `string: ${slots.basicString}`, + `number: ${slots.basicNumber}`, + `bigint: ${slots.basicBigint}`, + `boolean: ${slots.basicBoolean}`, + `symbol: ${slots.basicSymbol?.toString() ?? 'no symbol'}`, + + `html:`, + slots.basicHTML, + ]), + }); + + // 1-2: basic values match type, no error & reflected in content + + t.doesNotThrow( + () => template1.setSlots({ + basicString: 'pingas', + basicNumber: 123, + basicBigint: 1234567891234567n, + basicBoolean: true, + basicSymbol: Symbol(`sup`), + basicHTML: html.tag('span', `SnooPING AS usual, I see!`), + })); + + t.equal( + template1.toString(), + html.tag('p', [ + `string: pingas`, + `number: 123`, + `bigint: 1234567891234567`, + `boolean: true`, + `symbol: Symbol(sup)`, + `html:`, + html.tag('span', `SnooPING AS usual, I see!`), + ]).toString()); + + // 3-4: null matches any type, no error & reflected in content + + t.doesNotThrow( + () => template1.setSlots({ + basicString: null, + basicNumber: null, + basicBigint: null, + basicBoolean: null, + basicSymbol: null, + basicHTML: null, + })); + + t.equal( + template1.toString(), + html.tag('p', [ + `string: null`, + `number: null`, + `bigint: null`, + `boolean: null`, + `symbol: no symbol`, + `html:`, + ]).toString()); + + // 5-6: type mismatch throws error, invalidates entire setSlots call + + template1.setSlots({ + basicString: 'pingas', + basicNumber: 123, + }); + + strictlyThrows(t, + () => template1.setSlots({ + basicBoolean: false, + basicSymbol: `I'm not a symbol!`, + }), + new AggregateError([ + new TypeError(`(basicSymbol) Slot expects symbol, got string`), + ], `Error validating template slots`)) + + t.equal( + template1.toString(), + html.tag('p', [ + `string: pingas`, + `number: 123`, + `bigint: null`, + `boolean: null`, + `symbol: no symbol`, + `html:`, + ]).toString()); + + const template2 = html.template({ + slots: { + arrayOfStrings: { + validate: v => v.arrayOf(v.isString), + default: `Array Of Strings Fallback`.split(' '), + }, + + arrayOfHTML: { + validate: v => v.arrayOf(v.isHTML), + default: [], + }, + }, + + content: slots => + html.tag('p', [ + html.tag('strong', slots.arrayOfStrings), + `arrayOfHTML length: ${slots.arrayOfHTML.length}`, + ]), + }); + + // 7: isHTML behaves as it should, validate fails with validate throw + + strictlyThrows(t, + () => template2.setSlots({ + arrayOfStrings: ['you got it', 'pingas', 0xdeadbeef], + arrayOfHTML: [ + html.tag('span'), + html.template({content: () => 'dog'}), + html.blank(), + false && 'dogs', + null, + undefined, + html.tags([ + html.tag('span', 'usual'), + html.tag('span', 'i'), + ]), + ], + }), + new AggregateError([ + { + name: 'AggregateError', + message: /^\(arrayOfStrings\)/, + errors: {length: 1}, + }, + ], `Error validating template slots`)); + + // 8: default slot values respected + + t.equal( + template2.toString(), + html.tag('p', [ + html.tag('strong', [ + `Array`, + `Of`, + `Strings`, + `Fallback`, + ]), + `arrayOfHTML length: 0`, + ]).toString()); +}); |