diff --git a/README.md b/README.md index 667fd37..6dcfaf6 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,91 @@ # Horizr CLI > A CLI tool for creating Minecraft modpacks, primarily using the Fabric and Quilt loaders. + +🎉 Features: +- Search for mods on [Modrinth](https://modrinth.com/) +- Add mods from Modrinth +- View available (compatible) versions of mods from Modrinth +- View dependencies of specific mod versions +- Check for updates and view changelogs before applying them +- Export the pack to the [`packwiz`](https://packwiz.infra.link/) format +- HTTP-serve the `packwiz` export for usage with [`packwiz-installer`](https://packwiz.infra.link/tutorials/installing/packwiz-installer/) +- Export the pack to the [Modrinth format (`.mrpack`)](https://docs.modrinth.com/docs/modpacks/format_definition/) + +## Usage + +Because both [pkg](https://github.com/vercel/pkg) and [nexe](https://github.com/nexe/nexe) don’t support ES modules at the time of writing, +I can‘t publish executable files. + +The only way of installing is therefore `npm`. + +```sh +$ npm i -g @horizr/cli +``` + +Run any command with the `-h` flag to see the available options. + +## Examples + +- Activate the latest (compatible) version of [Charm](https://modrinth.com/mod/charm) +```sh +$ horizr modrinth mod activate charm + +# or short: +$ horizr mr mod a charm +``` + +- Activate `v4.1.1` of [Charm](https://modrinth.com/mod/charm) +```sh +$ horizr modrinth mod versions charm + +# `BT9G1Jjs` is the version code you are looking for. +# This output will be colored in your console. +BT9G1Jjs 4.2.0+1.18.2 (↓ 137) +featured + +Name: [1.18.2] 4.2.0 +Channel: release +Minecraft versions: 1.18.2 + +Publication: last week + +https://modrinth.com/mod/pOQTcQmj/version/BT9G1Jjs + +# … more versions omitted for brevity + +$ horizr modrinth version activate BT9G1Jjs + +Charm (4.2.0+1.18.2) was successfully activated. + + +Dependencies +◉ Fabric API (P7dR8mSH): any version + +``` + +- Check for updates +```sh +$ horizr update +# Because Sodium's version string is not a valid SemVer, +# the publication date will instead be used for comparison. +❯ Sodium has no valid semantic version: mc1.18.2-0.4.1. The +publication date will instead be used. + +Available updates +- charm Charm: 4.1.0+1.18.2 → 4.2.0+1.18.2 +``` + +```sh +$ horizr update charm + +Changelog for 4.2.0+1.18.2 + +* Added ebony wood. +* Fixed issue with Totems not always spawning or being +carried away by mobs. +# … omitted for brevity + +Apply the update? [Y/n] y + +Successfully updated Charm to 4.2.0+1.18.2. +``` diff --git a/bin/horizr.js b/bin/horizr.js new file mode 100755 index 0000000..5fe2beb --- /dev/null +++ b/bin/horizr.js @@ -0,0 +1,3 @@ +#!/usr/bin/env node + +import('../dist/main.js') diff --git a/package.json b/package.json index 2b77596..702b129 100644 --- a/package.json +++ b/package.json @@ -1,26 +1,22 @@ { - "name": "horizr-cli", + "name": "@horizr/cli", "version": "1.0.0", - "private": true, - "main": "./src/main.ts", + "main": "./dist/main.ts", "type": "module", "license": "MIT", + "author": "Moritz Ruth ", + "repository": "https://github.com/horizr/cli", "scripts": { - "start": "tsx .", - "build": "tsc" - }, - "oclif": { - "bin": "horizr", - "dirname": "horizr", - "commands": "./dist/commands", - "plugins": [ - "@oclif/plugin-plugins" - ], - "topicSeparator": " " + "start": "tsx src/main.ts", + "build": "del dist && tsc" }, "bin": { - "horizr": "./bin/horizr" + "horizr": "bin/horizr.js" }, + "files": [ + "dist", + "bin" + ], "dependencies": { "@root/walk": "^1.1.0", "address": "^1.2.0", @@ -45,6 +41,7 @@ "serve-handler": "^6.1.3", "wrap-ansi": "^8.0.1", "yazl": "^2.5.1", + "yesno": "^0.4.0", "zod": "^3.18.0" }, "devDependencies": { @@ -56,6 +53,7 @@ "@types/serve-handler": "^6.1.1", "@types/wrap-ansi": "^8.0.1", "@types/yazl": "^2.4.2", + "del-cli": "^5.0.0", "tsx": "^3.8.2", "type-fest": "^2.18.0", "typescript": "^4.7.4" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f65454d..13cd895 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,6 +13,7 @@ specifiers: address: ^1.2.0 commander: ^9.4.0 dedent: ^0.7.0 + del-cli: ^5.0.0 env-paths: ^3.0.0 figures: ^5.0.0 find-up: ^6.3.0 @@ -35,6 +36,7 @@ specifiers: typescript: ^4.7.4 wrap-ansi: ^8.0.1 yazl: ^2.5.1 + yesno: ^0.4.0 zod: ^3.18.0 dependencies: @@ -61,6 +63,7 @@ dependencies: serve-handler: 6.1.3 wrap-ansi: 8.0.1 yazl: 2.5.1 + yesno: 0.4.0 zod: 3.18.0 devDependencies: @@ -72,12 +75,34 @@ devDependencies: '@types/serve-handler': 6.1.1 '@types/wrap-ansi': 8.0.1 '@types/yazl': 2.4.2 + del-cli: 5.0.0 tsx: 3.8.2 type-fest: 2.18.0 typescript: 4.7.4 packages: + /@babel/code-frame/7.18.6: + resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.18.6 + dev: true + + /@babel/helper-validator-identifier/7.18.6: + resolution: {integrity: sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/highlight/7.18.6: + resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.18.6 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + /@esbuild-kit/cjs-loader/2.3.3: resolution: {integrity: sha512-Rt4O1mXlPEDVxvjsHLgbtHVdUXYK9C1/6ThpQnt7FaXIjUOsI6qhHYMgALhNnlIMZffag44lXd6Dqgx3xALbpQ==} dependencies: @@ -108,6 +133,27 @@ packages: dev: true optional: true + /@nodelib/fs.scandir/2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat/2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk/1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.13.0 + dev: true + /@root/walk/1.1.0: resolution: {integrity: sha512-FfXPAta9u2dBuaXhPRawBcijNC9rmKVApmbi6lIZyg36VR/7L02ytxoY5K/14PJlHqiBUoYII73cTlekdKTUOw==} dev: false @@ -167,9 +213,17 @@ packages: resolution: {integrity: sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==} dev: true + /@types/minimist/1.2.2: + resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==} + dev: true + /@types/node/18.7.3: resolution: {integrity: sha512-LJgzOEwWuMTBxHzgBR/fhhBOWrvBjvO+zPteUgbbuQi80rYIZHrk1mNbRUqPZqSLP2H7Rwt1EFLL/tNLD1Xx/w==} + /@types/normalize-package-data/2.4.1: + resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} + dev: true + /@types/responselike/1.0.0: resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} dependencies: @@ -201,11 +255,26 @@ packages: engines: {node: '>= 10.0.0'} dev: false + /aggregate-error/4.0.1: + resolution: {integrity: sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==} + engines: {node: '>=12'} + dependencies: + clean-stack: 4.2.0 + indent-string: 5.0.0 + dev: true + /ansi-regex/6.0.1: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} engines: {node: '>=12'} dev: false + /ansi-styles/3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: true + /ansi-styles/6.1.0: resolution: {integrity: sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==} engines: {node: '>=12'} @@ -216,9 +285,13 @@ packages: engines: {node: '>=0.10.0'} dev: false + /arrify/1.0.1: + resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} + engines: {node: '>=0.10.0'} + dev: true + /balanced-match/1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - dev: false /base64-js/1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -237,7 +310,13 @@ packages: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - dev: false + + /braces/3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true /buffer-crc32/0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} @@ -277,11 +356,42 @@ packages: responselike: 2.0.1 dev: false + /camelcase-keys/7.0.2: + resolution: {integrity: sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==} + engines: {node: '>=12'} + dependencies: + camelcase: 6.3.0 + map-obj: 4.3.0 + quick-lru: 5.1.1 + type-fest: 1.4.0 + dev: true + + /camelcase/6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + dev: true + + /chalk/2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: true + /chalk/5.0.1: resolution: {integrity: sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} dev: false + /clean-stack/4.2.0: + resolution: {integrity: sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==} + engines: {node: '>=12'} + dependencies: + escape-string-regexp: 5.0.0 + dev: true + /cli-cursor/4.0.0: resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -305,6 +415,16 @@ packages: engines: {node: '>=0.8'} dev: false + /color-convert/1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: true + + /color-name/1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: true + /commander/9.4.0: resolution: {integrity: sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw==} engines: {node: ^12.20.0 || >=14} @@ -320,7 +440,6 @@ packages: /concat-map/0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - dev: false /content-disposition/0.5.2: resolution: {integrity: sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==} @@ -346,6 +465,24 @@ packages: ms: 2.1.2 dev: false + /decamelize-keys/1.1.0: + resolution: {integrity: sha512-ocLWuYzRPoS9bfiSdDd3cxvrzovVMZnRDVEzAs+hWIVXGDbHxWMECij2OBuyB/An0FFW/nLuq6Kv1i/YC5Qfzg==} + engines: {node: '>=0.10.0'} + dependencies: + decamelize: 1.2.0 + map-obj: 1.0.1 + dev: true + + /decamelize/1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + dev: true + + /decamelize/5.0.1: + resolution: {integrity: sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==} + engines: {node: '>=10'} + dev: true + /decompress-response/6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} @@ -368,6 +505,36 @@ packages: engines: {node: '>=10'} dev: false + /del-cli/5.0.0: + resolution: {integrity: sha512-rENFhUaYcjoMODwFhhlON+ogN7DoG+4+GFN+bsA1XeDt4w2OKQnQadFP1thHSAlK9FAtl88qgP66wOV+eFZZiQ==} + engines: {node: '>=14.16'} + hasBin: true + dependencies: + del: 7.0.0 + meow: 10.1.3 + dev: true + + /del/7.0.0: + resolution: {integrity: sha512-tQbV/4u5WVB8HMJr08pgw0b6nG4RGt/tj+7Numvq+zqcvUFeMaIWWOUFltiU+6go8BSO2/ogsB4EasDaj0y68Q==} + engines: {node: '>=14.16'} + dependencies: + globby: 13.1.2 + graceful-fs: 4.2.10 + is-glob: 4.0.3 + is-path-cwd: 3.0.0 + is-path-inside: 4.0.0 + p-map: 5.5.0 + rimraf: 3.0.2 + slash: 4.0.0 + dev: true + + /dir-glob/3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + /eastasianwidth/0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} dev: false @@ -387,6 +554,12 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dev: false + /error-ex/1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + dependencies: + is-arrayish: 0.2.1 + dev: true + /esbuild-android-64/0.14.54: resolution: {integrity: sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==} engines: {node: '>=12'} @@ -596,10 +769,25 @@ packages: esbuild-windows-arm64: 0.14.54 dev: true + /escape-string-regexp/1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: true + /escape-string-regexp/5.0.0: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} - dev: false + + /fast-glob/3.2.11: + resolution: {integrity: sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true /fast-url-parser/1.1.3: resolution: {integrity: sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==} @@ -607,6 +795,12 @@ packages: punycode: 1.4.1 dev: false + /fastq/1.13.0: + resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==} + dependencies: + reusify: 1.0.4 + dev: true + /figures/5.0.0: resolution: {integrity: sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==} engines: {node: '>=14'} @@ -615,6 +809,21 @@ packages: is-unicode-supported: 1.2.0 dev: false + /fill-range/7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /find-up/5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + /find-up/6.3.0: resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -645,6 +854,10 @@ packages: universalify: 0.1.2 dev: false + /fs.realpath/1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + /fsevents/2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -653,6 +866,10 @@ packages: dev: true optional: true + /function-bind/1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + dev: true + /get-stream/5.2.0: resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} engines: {node: '>=8'} @@ -669,6 +886,35 @@ packages: resolution: {integrity: sha512-X8u8fREiYOE6S8hLbq99PeykTDoLVnxvF4DjWKJmz9xy2nNRdUcV8ZN9tniJFeKyTU3qnC9lL8n4Chd6LmVKHg==} dev: true + /glob-parent/5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob/7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /globby/13.1.2: + resolution: {integrity: sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + dir-glob: 3.0.1 + fast-glob: 3.2.11 + ignore: 5.2.0 + merge2: 1.4.1 + slash: 4.0.0 + dev: true + /got/12.3.1: resolution: {integrity: sha512-tS6+JMhBh4iXMSXF6KkIsRxmloPln31QHDlcb6Ec3bzxjjFJFr/8aXdpyuLmVc9I4i2HyBHYw1QU5K1ruUdpkw==} engines: {node: '>=14.16'} @@ -690,7 +936,23 @@ packages: /graceful-fs/4.2.10: resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} - dev: false + + /hard-rejection/2.1.0: + resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} + engines: {node: '>=6'} + dev: true + + /has-flag/3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + dev: true + + /has/1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + dev: true /hasha/5.2.2: resolution: {integrity: sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==} @@ -700,6 +962,13 @@ packages: type-fest: 0.8.1 dev: false + /hosted-git-info/4.1.0: + resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} + engines: {node: '>=10'} + dependencies: + lru-cache: 6.0.0 + dev: true + /http-cache-semantics/4.1.0: resolution: {integrity: sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==} dev: false @@ -716,15 +985,73 @@ packages: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} dev: false + /ignore/5.2.0: + resolution: {integrity: sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==} + engines: {node: '>= 4'} + dev: true + + /indent-string/5.0.0: + resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} + engines: {node: '>=12'} + dev: true + + /inflight/1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + /inherits/2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - dev: false + + /is-arrayish/0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + dev: true + + /is-core-module/2.10.0: + resolution: {integrity: sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==} + dependencies: + has: 1.0.3 + dev: true + + /is-extglob/2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-glob/4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true /is-interactive/2.0.0: resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} engines: {node: '>=12'} dev: false + /is-number/7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-path-cwd/3.0.0: + resolution: {integrity: sha512-kyiNFFLU0Ampr6SDZitD/DwUo4Zs1nSdnygUBqsu3LooL00Qvb5j+UnvApUn/TTj1J3OuE6BTdQ5rudKmU2ZaA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + + /is-path-inside/4.0.0: + resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} + engines: {node: '>=12'} + dev: true + + /is-plain-obj/1.1.0: + resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} + engines: {node: '>=0.10.0'} + dev: true + /is-stream/2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -735,10 +1062,18 @@ packages: engines: {node: '>=12'} dev: false + /js-tokens/4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: true + /json-buffer/3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} dev: false + /json-parse-even-better-errors/2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + dev: true + /jsonfile/4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} optionalDependencies: @@ -770,11 +1105,27 @@ packages: json-buffer: 3.0.1 dev: false + /kind-of/6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + dev: true + /kleur/4.1.5: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} dev: false + /lines-and-columns/1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + dev: true + + /locate-path/6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + /locate-path/7.1.1: resolution: {integrity: sha512-vJXaRMJgRVD3+cUZs3Mncj2mxpt5mP0EmNOsxRSZRMlbqjvxzDEOIUWXGmavo0ZC9+tNZCBLQ66reA11nbpHZg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -817,7 +1168,47 @@ packages: engines: {node: '>=10'} dependencies: yallist: 4.0.0 - dev: false + + /map-obj/1.0.1: + resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} + engines: {node: '>=0.10.0'} + dev: true + + /map-obj/4.3.0: + resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} + engines: {node: '>=8'} + dev: true + + /meow/10.1.3: + resolution: {integrity: sha512-0WL7RMCPPdUTE00+GxJjL4d5Dm6eUbmAzxlzywJWiRUKCW093owmZ7/q74tH9VI91vxw9KJJNxAcvdpxb2G4iA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + '@types/minimist': 1.2.2 + camelcase-keys: 7.0.2 + decamelize: 5.0.1 + decamelize-keys: 1.1.0 + hard-rejection: 2.1.0 + minimist-options: 4.1.0 + normalize-package-data: 3.0.3 + read-pkg-up: 8.0.0 + redent: 4.0.0 + trim-newlines: 4.0.2 + type-fest: 1.4.0 + yargs-parser: 20.2.9 + dev: true + + /merge2/1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch/4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true /mime-db/1.33.0: resolution: {integrity: sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==} @@ -846,12 +1237,32 @@ packages: engines: {node: '>=10'} dev: false + /min-indent/1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + dev: true + /minimatch/3.0.4: resolution: {integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==} dependencies: brace-expansion: 1.1.11 dev: false + /minimatch/3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimist-options/4.1.0: + resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} + engines: {node: '>= 6'} + dependencies: + arrify: 1.0.1 + is-plain-obj: 1.1.0 + kind-of: 6.0.3 + dev: true + /ms/2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} dev: false @@ -862,6 +1273,16 @@ packages: hasBin: true dev: false + /normalize-package-data/3.0.3: + resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} + engines: {node: '>=10'} + dependencies: + hosted-git-info: 4.1.0 + is-core-module: 2.10.0 + semver: 7.3.7 + validate-npm-package-license: 3.0.4 + dev: true + /normalize-url/6.1.0: resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} engines: {node: '>=10'} @@ -871,7 +1292,6 @@ packages: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: wrappy: 1.0.2 - dev: false /onetime/5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} @@ -907,6 +1327,13 @@ packages: p-timeout: 5.1.0 dev: false + /p-limit/3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + /p-limit/4.0.0: resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -914,6 +1341,13 @@ packages: yocto-queue: 1.0.0 dev: false + /p-locate/5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + /p-locate/6.0.0: resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -921,16 +1355,43 @@ packages: p-limit: 4.0.0 dev: false + /p-map/5.5.0: + resolution: {integrity: sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg==} + engines: {node: '>=12'} + dependencies: + aggregate-error: 4.0.1 + dev: true + /p-timeout/5.1.0: resolution: {integrity: sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==} engines: {node: '>=12'} dev: false + /parse-json/5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + dependencies: + '@babel/code-frame': 7.18.6 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + dev: true + + /path-exists/4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + /path-exists/5.0.0: resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dev: false + /path-is-absolute/1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + /path-is-inside/1.0.2: resolution: {integrity: sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==} dev: false @@ -939,6 +1400,16 @@ packages: resolution: {integrity: sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==} dev: false + /path-type/4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /picomatch/2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + /pump/3.0.0: resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} dependencies: @@ -950,16 +1421,38 @@ packages: resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} dev: false + /queue-microtask/1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + /quick-lru/5.1.1: resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} engines: {node: '>=10'} - dev: false /range-parser/1.2.0: resolution: {integrity: sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==} engines: {node: '>= 0.6'} dev: false + /read-pkg-up/8.0.0: + resolution: {integrity: sha512-snVCqPczksT0HS2EC+SxUndvSzn6LRCwpfSvLrIfR5BKDQQZMaI6jPRC9dYvYFDRAuFEAnkwww8kBBNE/3VvzQ==} + engines: {node: '>=12'} + dependencies: + find-up: 5.0.0 + read-pkg: 6.0.0 + type-fest: 1.4.0 + dev: true + + /read-pkg/6.0.0: + resolution: {integrity: sha512-X1Fu3dPuk/8ZLsMhEj5f4wFAF0DWoK7qhGJvgaijocXxBmSToKfbFtqbxMO7bVjNA1dmE5huAzjXj/ey86iw9Q==} + engines: {node: '>=12'} + dependencies: + '@types/normalize-package-data': 2.4.1 + normalize-package-data: 3.0.3 + parse-json: 5.2.0 + type-fest: 1.4.0 + dev: true + /readable-stream/3.6.0: resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==} engines: {node: '>= 6'} @@ -969,6 +1462,14 @@ packages: util-deprecate: 1.0.2 dev: false + /redent/4.0.0: + resolution: {integrity: sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==} + engines: {node: '>=12'} + dependencies: + indent-string: 5.0.0 + strip-indent: 4.0.0 + dev: true + /resolve-alpn/1.2.1: resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} dev: false @@ -987,6 +1488,24 @@ packages: signal-exit: 3.0.7 dev: false + /reusify/1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rimraf/3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /run-parallel/1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + /s-ago/2.2.0: resolution: {integrity: sha512-t6Q/aFCCJSBf5UUkR/WH0mDHX8EGm2IBQ7nQLobVLsdxOlkryYMbOlwu2D4Cf7jPUp0v1LhfPgvIZNoi9k8lUA==} dev: false @@ -1001,7 +1520,6 @@ packages: hasBin: true dependencies: lru-cache: 6.0.0 - dev: false /serve-handler/6.1.3: resolution: {integrity: sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w==} @@ -1020,6 +1538,11 @@ packages: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} dev: false + /slash/4.0.0: + resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} + engines: {node: '>=12'} + dev: true + /source-map-support/0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} dependencies: @@ -1032,6 +1555,28 @@ packages: engines: {node: '>=0.10.0'} dev: true + /spdx-correct/3.1.1: + resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==} + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.11 + dev: true + + /spdx-exceptions/2.3.0: + resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==} + dev: true + + /spdx-expression-parse/3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + dependencies: + spdx-exceptions: 2.3.0 + spdx-license-ids: 3.0.11 + dev: true + + /spdx-license-ids/3.0.11: + resolution: {integrity: sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==} + dev: true + /string-width/5.1.2: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} engines: {node: '>=12'} @@ -1054,6 +1599,32 @@ packages: ansi-regex: 6.0.1 dev: false + /strip-indent/4.0.0: + resolution: {integrity: sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==} + engines: {node: '>=12'} + dependencies: + min-indent: 1.0.1 + dev: true + + /supports-color/5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + dev: true + + /to-regex-range/5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /trim-newlines/4.0.2: + resolution: {integrity: sha512-GJtWyq9InR/2HRiLZgpIKv+ufIKrVrvjQWEj7PxAXNc5dwbNJkqhAUoAGgzRmULAnoOM5EIpveYd3J2VeSAIew==} + engines: {node: '>=12'} + dev: true + /tslib/1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} dev: false @@ -1074,6 +1645,11 @@ packages: engines: {node: '>=8'} dev: false + /type-fest/1.4.0: + resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} + engines: {node: '>=10'} + dev: true + /type-fest/2.18.0: resolution: {integrity: sha512-pRS+/yrW5TjPPHNOvxhbNZexr2bS63WjrMU8a+VzEBhUi9Tz1pZeD+vQz3ut0svZ46P+SRqMEPnJmk2XnvNzTw==} engines: {node: '>=12.20'} @@ -1099,6 +1675,13 @@ packages: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: false + /validate-npm-package-license/3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + dependencies: + spdx-correct: 3.1.1 + spdx-expression-parse: 3.0.1 + dev: true + /wcwidth/1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} dependencies: @@ -1116,11 +1699,14 @@ packages: /wrappy/1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - dev: false /yallist/4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - dev: false + + /yargs-parser/20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + dev: true /yazl/2.5.1: resolution: {integrity: sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==} @@ -1128,6 +1714,15 @@ packages: buffer-crc32: 0.2.13 dev: false + /yesno/0.4.0: + resolution: {integrity: sha512-tdBxmHvbXPBKYIg81bMCB7bVeDmHkRzk5rVJyYYXurwKkHq/MCd8rz4HSJUP7hW0H2NlXiq8IFiWvYKEHhlotA==} + dev: false + + /yocto-queue/0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true + /yocto-queue/1.0.0: resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} engines: {node: '>=12.20'} diff --git a/src/commands/modrinth.ts b/src/commands/modrinth.ts index a2f62d0..ab31156 100644 --- a/src/commands/modrinth.ts +++ b/src/commands/modrinth.ts @@ -191,7 +191,8 @@ const modrinthVersionCommand = modrinthCommand.command("version") modrinthVersionCommand.command("info ") .description("Show information about the version.") - .action(async id => { + .option("-c, --changelog", "Show the changelog.") + .action(async (id, options) => { const pack = await usePack() const loader = output.startLoading("Fetching version information") @@ -244,6 +245,13 @@ modrinthVersionCommand.command("info ") https://modrinth.com/mod/${modrinthMod.slug}/version/${modrinthVersion.versionString} `) + + if (options.changelog) { + output.println("") + output.println(kleur.underline("Changelog")) + if (modrinthVersion.changelog === null) output.println(kleur.gray("not available")) + else output.printlnWrapping(modrinthVersion.changelog) + } }) modrinthVersionCommand.command("activate ") diff --git a/src/commands/packwiz.ts b/src/commands/packwiz.ts index a6cdbfc..8ac6b7e 100644 --- a/src/commands/packwiz.ts +++ b/src/commands/packwiz.ts @@ -1,5 +1,5 @@ import { Command } from "commander" -import { usePack } from "../pack.js" +import { Mod, usePack } from "../pack.js" import fs from "fs-extra" import dedent from "dedent" import kleur from "kleur" @@ -9,12 +9,7 @@ import { Visitor, walk } from "@root/walk" import { Path } from "../path.js" const packwizCommand = new Command("packwiz") - -interface IndexedFile { - path: string - sha512HashHex: string - isMeta: boolean -} + .alias("pw") packwizCommand.command("import") .description("Import a packwiz pack.") @@ -33,135 +28,177 @@ packwizCommand.command("serve") if (!(await fs.pathExists(directoryPath.toString()))) output.failAndExit(`The ${kleur.yellow("packwiz")} directory does not exist. Generate it by running ${kleur.yellow("horizr packwiz export")}.`) - const lanAddress = await getLANAddress() - - httpServeDirectory(directoryPath, options.port, options.expose, () => { - const localAddress = `http://localhost:${options.port}/pack.toml` - - if (options.expose) { - output.println(dedent` - ${kleur.green("Serving at")} - Local: ${kleur.yellow(localAddress)} - Network: ${kleur.yellow(`http://${lanAddress}:${options.port}/pack.toml`)} - `) - } - else output.println(`${kleur.green("Serving at")} ${kleur.yellow(localAddress)}`) - }) + await serveExportOutput(directoryPath, options.port, options.expose) }) -packwizCommand.command("dev") - .description("serve + export with hot-reloading.") - .action(async () => { - output.failAndExit("Not implemented.") - // TODO: serve and export with hot-reloading - }) +// packwizCommand.command("dev") +// .description("serve + export with hot-reloading.") +// .option("-s, --server", "Use server overrides instead of client overrides.") +// .option("-p, --port ", "The port of the HTTP server.", optionParsePositiveInteger, 8000) +// .option("-e, --expose", "Expose the HTTP server on all interfaces.") +// .action(async options => { +// +// }) packwizCommand.command("export") .description("Generate a packwiz pack in the packwiz directory.") .option("-s, --server", "Use server overrides instead of client overrides.") .action(async options => { - const pack = await usePack() - - if (pack.horizrFile.loader !== "fabric") - output.println(kleur.yellow(`packwiz does not yet support the ${kleur.reset(pack.horizrFile.loader)} loader. No loader will be specified.`)) - - const loader = output.startLoading("Generating") - - const outputDirectoryPath = pack.paths.generated.resolve("packwiz") - const modsDirectoryPath = outputDirectoryPath.resolve("mods") - await fs.remove(outputDirectoryPath.toString()) - await fs.mkdirp(modsDirectoryPath.toString()) - - const indexedFiles: IndexedFile[] = [] - for (const mod of pack.mods) { - if (!mod.modFile.enabled) output.warn(`${kleur.yellow(mod.modFile.name)} is disabled and will not be included.`) - const innerLoader = output.startLoading(`Generating ${kleur.yellow(mod.id + ".toml")} (${indexedFiles.length + 1}/${pack.mods.length})`) - - const content = dedent` - name = ${JSON.stringify(mod.modFile.name)} - filename = ${JSON.stringify(mod.modFile.file.name)} - side = "${mod.modFile.side.replace("client+server", "both")}" - - [download] - hash-format = "sha512" - hash = ${JSON.stringify(mod.modFile.file.hashes.sha512)} - url = ${JSON.stringify(mod.modFile.file.downloadUrl)} - ` - - const path = modsDirectoryPath.resolve(mod.id + ".toml") - await fs.writeFile(path.toString(), content) - - indexedFiles.push({ - path: outputDirectoryPath.relative(path).toString(), - isMeta: true, - sha512HashHex: await getSha512HexHash(content) - }) - - innerLoader.stop() - } - - loader.setText(`Copying and hashing ${options.server ? "server" : "client"} overrides`) - - const createVisitor = (overridesDirectoryPath: Path): Visitor => async (error, path, dirent) => { - const relativePath = overridesDirectoryPath.relative(Path.create(path)) - - if (error) output.warn(`${kleur.yellow(relativePath.toString())}: ${error.message}`) - else { - if (dirent.name.startsWith(".")) return false - if (dirent.isFile()) { - const outputPath = outputDirectoryPath.resolve(relativePath) - await fs.mkdirp(outputPath.getParent().toString()) - await fs.copy(path, outputPath.toString()) - - indexedFiles.push({ - path: relativePath.toString(), - isMeta: false, - sha512HashHex: await getSha512HexHash(await fs.readFile(overridesDirectoryPath.resolve(path).toString())) - }) - } - } - } - - const specificOverridesDirectoryPath = pack.paths.overrides[options.server ? "server" : "client"] - const universalOverridesDirectoryPath = pack.paths.overrides["client-server"] - - if (await fs.pathExists(specificOverridesDirectoryPath.toString())) await walk(specificOverridesDirectoryPath.toString(), createVisitor(specificOverridesDirectoryPath)) - if (await fs.pathExists(universalOverridesDirectoryPath.toString())) await walk(universalOverridesDirectoryPath.toString(), createVisitor(universalOverridesDirectoryPath)) - - loader.setText(`Writing ${kleur.yellow("index.toml")}`) - - const index = dedent` - hash-format = "sha512" - - ${indexedFiles.map(file => dedent` - [[files]] - file = ${JSON.stringify(file.path)} - hash = "${file.sha512HashHex}" - metafile = ${file.isMeta} - `).join("\n\n")} - ` - - await fs.writeFile(outputDirectoryPath.resolve("index.toml").toString(), index) - const indexHash = await getSha512HexHash(index) - - await fs.writeFile(outputDirectoryPath.resolve("pack.toml").toString(), dedent` - name = ${JSON.stringify(pack.horizrFile.meta.name)} - authors = ${JSON.stringify(pack.horizrFile.meta.authors.join(", "))}\ - ${pack.horizrFile.meta.description === undefined ? "" : "\n" + `description = ${JSON.stringify(pack.horizrFile.meta.description)}`} - pack-format = "packwiz:1.0.0" - - [versions] - minecraft = "${pack.horizrFile.versions.minecraft}"\ - ${pack.horizrFile.loader === "fabric" ? "\n" + `fabric = ${JSON.stringify(pack.horizrFile.versions.loader)}` : ""} - - [index] - file = "index.toml" - hash-format = "sha512" - hash = "${indexHash}" - `) - - loader.stop() - output.println(kleur.green("Generated packwiz pack")) + await runExport(options.server) }) +async function runExport(forServer: boolean) { + const pack = await usePack() + + if (pack.horizrFile.loader !== "fabric") + output.println(kleur.yellow(`packwiz does not yet support the ${kleur.reset(pack.horizrFile.loader)} loader. No loader will be specified.`)) + + const loader = output.startLoading("Generating") + + const outputDirectoryPath = pack.paths.generated.resolve("packwiz") + await fs.remove(outputDirectoryPath.toString()) + await fs.mkdirp(outputDirectoryPath.resolve("mods").toString()) + + const indexedFiles: IndexedFile[] = [] + await indexMods(indexedFiles, outputDirectoryPath) + + loader.setText(`Copying and hashing ${forServer ? "server" : "client"} overrides`) + await copyOverrides(indexedFiles, outputDirectoryPath, forServer) + + loader.setText(`Writing ${kleur.yellow("index.toml")}`) + + await writeIndexAndPackManifest(indexedFiles, outputDirectoryPath) + + loader.stop() + output.println(kleur.green("Generated packwiz pack")) + + return { + indexedFiles + } +} + +interface IndexedFile { + path: string + sha512HashHex: string + isMeta: boolean +} + +async function writeAndIndexModMetaFile(indexedFiles: IndexedFile[], outputDirectoryPath: Path, mod: Mod) { + const content = dedent` + name = ${JSON.stringify(mod.modFile.name)} + filename = ${JSON.stringify(mod.modFile.file.name)} + side = "${mod.modFile.side.replace("client-server", "both")}" + + [download] + hash-format = "sha512" + hash = ${JSON.stringify(mod.modFile.file.hashes.sha512)} + url = ${JSON.stringify(mod.modFile.file.downloadUrl)} + ` + + const path = outputDirectoryPath.resolve(`mods/${mod.id}.toml`) + await fs.writeFile(path.toString(), content) + + indexedFiles.push({ + path: `mods/${mod.id}.toml`, + isMeta: true, + sha512HashHex: await getSha512HexHash(content) + }) +} + +async function indexMods(indexedFiles: IndexedFile[], outputDirectoryPath: Path, warn: boolean = true) { + const pack = await usePack() + + for (const mod of pack.mods) { + if (warn && !mod.modFile.enabled) output.warn(`${kleur.yellow(mod.modFile.name)} is disabled and will not be included.`) + + await output.withLoading( + writeAndIndexModMetaFile(indexedFiles, outputDirectoryPath, mod), + `Generating ${kleur.yellow(mod.id + ".toml")} (${indexedFiles.length + 1}/${pack.mods.length})` + ) + } +} + +async function copyOverrides(indexedFiles: IndexedFile[], outputDirectoryPath:Path, forServer: boolean) { + const pack = await usePack() + + const createVisitor = (overridesDirectoryPath: Path): Visitor => async (error, path, dirent) => { + const relativePath = overridesDirectoryPath.relative(path) + + if (error) output.warn(`${kleur.yellow(relativePath.toString())}: ${error.message}`) + else { + if (dirent.name.startsWith(".")) return false + if (dirent.isFile()) { + const outputPath = outputDirectoryPath.resolve(relativePath) + await fs.mkdirp(outputPath.getParent().toString()) + await fs.copy(path, outputPath.toString()) + + indexedFiles.push({ + path: relativePath.toString(), + isMeta: false, + sha512HashHex: await getSha512HexHash(await fs.readFile(overridesDirectoryPath.resolve(path).toString())) + }) + } + } + } + + const specificOverridesDirectoryPath = pack.paths.overrides[forServer ? "server" : "client"] + const universalOverridesDirectoryPath = pack.paths.overrides["client-server"] + + if (await fs.pathExists(specificOverridesDirectoryPath.toString())) await walk(specificOverridesDirectoryPath.toString(), createVisitor(specificOverridesDirectoryPath)) + if (await fs.pathExists(universalOverridesDirectoryPath.toString())) await walk(universalOverridesDirectoryPath.toString(), createVisitor(universalOverridesDirectoryPath)) +} + +async function writeIndexAndPackManifest(indexedFiles: IndexedFile[], outputDirectoryPath: Path) { + const pack = await usePack() + + const index = dedent` + hash-format = "sha512" + + ${indexedFiles.map(file => dedent` + [[files]] + file = ${JSON.stringify(file.path)} + hash = "${file.sha512HashHex}" + metafile = ${file.isMeta} + `).join("\n\n")} + ` + + await fs.writeFile(outputDirectoryPath.resolve("index.toml").toString(), index) + const indexHash = await getSha512HexHash(index) + + await fs.writeFile(outputDirectoryPath.resolve("pack.toml").toString(), dedent` + name = ${JSON.stringify(pack.horizrFile.meta.name)} + authors = ${JSON.stringify(pack.horizrFile.meta.authors.join(", "))}\ + ${pack.horizrFile.meta.description === undefined ? "" : "\n" + `description = ${JSON.stringify(pack.horizrFile.meta.description)}`} + pack-format = "packwiz:1.0.0" + + [versions] + minecraft = "${pack.horizrFile.versions.minecraft}"\ + ${pack.horizrFile.loader === "fabric" ? "\n" + `fabric = ${JSON.stringify(pack.horizrFile.versions.loader)}` : ""} + + [index] + file = "index.toml" + hash-format = "sha512" + hash = "${indexHash}" + `) +} + +async function serveExportOutput(path: Path, port: number, expose: boolean) { + const lanAddress = await getLANAddress() + const localAddress = `http://localhost:${port}/pack.toml` + + await new Promise(resolve => { + httpServeDirectory(path, port, expose, () => { + if (expose) { + output.println(dedent` + ${kleur.green("Serving at")} + Local: ${kleur.yellow(localAddress)} + Network: ${kleur.yellow(`http://${lanAddress}:${port}/pack.toml`)} + `) + } else output.println(`${kleur.green("Serving at")} ${kleur.yellow(localAddress)}`) + + resolve() + }) + }) +} + export { packwizCommand} diff --git a/src/main.ts b/src/main.ts index 4d7798f..54d9d15 100644 --- a/src/main.ts +++ b/src/main.ts @@ -9,9 +9,16 @@ import { default as wrapAnsi } from "wrap-ansi" import { removeModFile } from "./files.js" import { output } from "./output.js" import figures from "figures" +import yesno from "yesno" import { releaseChannelOrder } from "./shared.js" +import fs from "fs-extra" +import { Path } from "./path.js" const program = new Command("horizr") + .version( + (await fs.readJson(Path.create(import.meta.url.slice(5)).getParent().resolve("../package.json").toString())).version, + "-v, --version" + ) program.command("info", { isDefault: true }) .description("Print information about the pack.") @@ -65,18 +72,31 @@ program.command("update [code]") `) } } else { - const loader = output.startLoading("Checking for an update") const mod = pack.findModByCodeOrFail(code) - const update = await mod.checkForUpdate(allowedReleaseChannels) + const update = await output.withLoading(mod.checkForUpdate(allowedReleaseChannels), "Checking for an update") if (update === null) { - loader.stop() output.println(kleur.green("No update available.")) } else { - loader.setText("Updating") - await update.apply() - loader.stop() - output.println(kleur.green(`Successfully updated ${kleur.yellow(update.mod.modFile.name)} to ${kleur.yellow(update.availableVersion)}.`)) + if (update.changelog === null) { + output.println(`No changelog available for ${kleur.bold(update.availableVersion)}.`) + } else { + output.println(`${kleur.underline("Changelog")} for ${kleur.bold().yellow(update.availableVersion)}\n`) + output.printlnWrapping(update.changelog) + } + + output.println("") + + const confirmed = options.yes || await yesno({ + question: "Apply the update? [Y/n]", + defaultValue: true, + invalid: () => {} + }) + + if (confirmed) { + await output.withLoading(update.apply(), "Updating") + output.println(kleur.green(`Successfully updated ${kleur.yellow(update.mod.modFile.name)} to ${kleur.yellow(update.availableVersion)}.`)) + } } } }) @@ -85,7 +105,7 @@ loudRejection(stack => { output.failAndExit(stack) }) -program +await program .addCommand(packwizCommand) .addCommand(modrinthCommand) .addHelpText("after", "\n" + dedent` @@ -95,6 +115,3 @@ program - The ID of a Modrinth Version, prefixed with ${kleur.yellow("mrv:")} `) .parseAsync(process.argv) - .catch(error => { - output.failAndExit(error.message) - }) diff --git a/src/modrinth/utils.ts b/src/modrinth/utils.ts index 26d3afc..67413e4 100644 --- a/src/modrinth/utils.ts +++ b/src/modrinth/utils.ts @@ -50,7 +50,7 @@ export async function addModrinthMod(modrinthMod: ModrinthMod, modrinthVersion: const pack = await usePack() let id = modrinthMod.slug - if (await pathExists(pack.rootDirectoryPath.resolve("mods", `${id}.json`).toString())) { + if (await pathExists(pack.paths.mods.resolve(`${id}.json`).toString())) { const oldId = id id = `${id}-${nanoid(5)}` @@ -67,7 +67,7 @@ export async function addModrinthMod(modrinthMod: ModrinthMod, modrinthVersion: name: modrinthMod.title, enabled: true, ignoreUpdates: false, - side: isClientSupported && isServerSupported ? "client+server" : isClientSupported ? "client" : "server", + side: isClientSupported && isServerSupported ? "client-server" : isClientSupported ? "client" : "server", file: getModFileDataForModrinthVersion(modrinthMod, modrinthVersion), source: { type: "modrinth", diff --git a/src/output.ts b/src/output.ts index 59213fe..371cac5 100644 --- a/src/output.ts +++ b/src/output.ts @@ -64,8 +64,8 @@ export const output = { text, spinner: ora({ spinner: "dots4", - color: "blue", - prefixText: "\n" + // prefixText: "\n" + color: "blue" }), fail(message?: string) { if (this.state !== "running") throw new Error("state is not 'running'") @@ -147,6 +147,7 @@ export const output = { }, warn(text: string) { this.printlnWrapping(`${kleur.yellow(figures.pointer)} ${text}`) + this.println("") }, fail(text: string) { last(loadersStack)?.fail() diff --git a/src/pack.ts b/src/pack.ts index f26940d..e4d7764 100644 --- a/src/pack.ts +++ b/src/pack.ts @@ -12,12 +12,14 @@ export interface Update { mod: Mod activeVersion: string availableVersion: string + changelog: string | null apply(): Promise } export interface Pack { paths: { root: Path, + mods: Path, generated: Path, overrides: Record }, @@ -52,6 +54,7 @@ export async function usePack(): Promise { paths: { root: rootDirectoryPath, generated: rootDirectoryPath.resolve("generated"), + mods: rootDirectoryPath.resolve("mods"), overrides: { client: overridesDirectoryPath.resolve("client"), server: overridesDirectoryPath.resolve("server"), @@ -96,6 +99,7 @@ export async function usePack(): Promise { mod, activeVersion: activeVersionString, availableVersion: newestVersion.versionString, + changelog: newestVersion.changelog, async apply() { const modrinthMod = (await modrinthApi.getMod(newestVersion.projectId))! diff --git a/src/path.ts b/src/path.ts index d6a1b86..caeb333 100644 --- a/src/path.ts +++ b/src/path.ts @@ -22,8 +22,8 @@ export class Path { /** * Returns the relative path from this path to the other path. */ - relative(other: Path) { - return new Path(pathModule.relative(this.value, other.value)) + relative(other: Path | string) { + return new Path(pathModule.relative(this.value, typeof other === "string" ? other : other.toString())) } getParent() { diff --git a/src/utils.ts b/src/utils.ts index 3ef99c3..45aced1 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -11,9 +11,28 @@ import addressWithCallback from "address" import { promisify } from "util" const address = promisify(addressWithCallback) - export const getLANAddress = () => address().then(r => r.ip) +export function createSingleConcurrencyWithQueue(fn: () => Promise) { + let state: "inactive" | "running_fresh" | "running_old" = "inactive" + + return async () => { + if (state === "inactive") { + const loop = () => { + state = "running_fresh" + + fn().then(() => { + if (state === "running_old") loop() + }) + } + + loop() + } else { + state = "running_old" + } + } +} + export function httpServeDirectory(path: Path, port: number, expose: boolean, onListen: () => void) { const server = http.createServer((request, response) => { return serveHandler(request, response, { @@ -46,7 +65,7 @@ export async function zipDirectory(directoryPath: Path, outputFilePath: Path) { if (directoryPath.toString() === path) return true if (dirent.name.startsWith(".")) return false - if (dirent.isFile()) zipFile.addFile(path, directoryPath.relative(Path.create(path)).toString(), { compress: true }) + if (dirent.isFile()) zipFile.addFile(path, directoryPath.relative(path).toString(), { compress: true }) }) zipFile.end()