diff --git a/bun.lock b/bun.lock index c484491..3ddc93d 100644 --- a/bun.lock +++ b/bun.lock @@ -4,6 +4,8 @@ "": { "name": "packwizjs", "dependencies": { + "@types/semver": "^7.5.8", + "semver": "^7.7.1", "toml": "^3.0.0", }, "devDependencies": { @@ -53,6 +55,8 @@ "@types/node": ["@types/node@22.13.1", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew=="], + "@types/semver": ["@types/semver@7.5.8", "", {}, "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ=="], + "@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="], "acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="], @@ -365,7 +369,7 @@ "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="], - "semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="], "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], @@ -443,18 +447,14 @@ "@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="], - "builtins/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="], - - "eslint-compat-utils/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="], - "eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], "eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], "eslint-plugin-import/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], - "eslint-plugin-n/globals": ["globals@13.24.0", "", { "dependencies": { "type-fest": "^0.20.2" } }, "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ=="], + "eslint-plugin-import/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - "eslint-plugin-n/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="], + "eslint-plugin-n/globals": ["globals@13.24.0", "", { "dependencies": { "type-fest": "^0.20.2" } }, "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ=="], } } diff --git a/package.json b/package.json index 75587d1..ce2feb0 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,8 @@ "typescript": "^5.0.0" }, "dependencies": { + "@types/semver": "^7.5.8", + "semver": "^7.7.1", "toml": "^3.0.0" } } diff --git a/src/parser.ts b/src/parser.ts index 1199562..f4b88af 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -8,6 +8,7 @@ import { } from "./provider"; import { HashFormat, isValidHashFormat } from "./enums/hash-format"; import { isValidSide, type Side } from "./enums/side"; +import * as semver from "semver"; export interface Metafile { name: string; @@ -112,46 +113,97 @@ export class IndexFileEntry { */ export interface PackwizIndex { location: Resource; + hash: string; hashFormat: HashFormat; files: IndexFileEntry[]; } -// TODO: parse pack.toml too instead of just the index file -// This is because pack.toml contains important information like modloader versions +export interface PackwizVersions { + minecraft: string; + fabric?: string; + forge?: string; + neoforge?: string; + quilt?: string; + liteloader?: string; +} + +export class Packwiz { + constructor( + readonly location: Resource, + readonly index: PackwizIndex, + readonly name: string, + readonly packFormat: string = "packwiz:1.0.0", + readonly authors: Array, + readonly description: string, + readonly version: string, + readonly versions: PackwizVersions, + ) { + const packwizJsMaxSupportedVersion = "1.1.0"; + const packFormatSemver = semver.clean(packFormat.split(":")[1]); + if (!packFormat.startsWith("packwiz:")) { + throw new Error("Invalid packwiz pack format!"); + } else if (!packFormatSemver) { + throw new Error("Unable to parse pack format!"); + } else if (semver.gt(packFormatSemver, packwizJsMaxSupportedVersion)) { + throw new Error( + `Pack format of ${packFormat} is higher than what PackwizJS supports! Please report this upstream at https://www.coastalcommits.com/GalacticFactory/PackwizJS/issues`, + ); + } + } +} /** - * Parses a Packwiz index file and returns its contents. + * Parses a packwiz.toml file and returns its contents. * * @param indexFilePath - The path of the TOML file. - * @returns The parsed index as a structured object. + * @returns The parsed file as a structured class. */ -export async function parsePackwizIndex( - indexFilePath: string, -): Promise { - const indexFile = new Resource(indexFilePath); - const parsed = toml.parse(await indexFile.fetchContents()); - const hashFormat = isValidHashFormat(parsed["hash-format"]); - return { - location: indexFile, - hashFormat: hashFormat, - files: parsed.files.map( - (file: { - file: string; - hash: string; - hashFormat: HashFormat; - alias?: string; - metafile?: boolean; - preserve?: boolean; - }) => { - return new IndexFileEntry( - indexFile.parent.join(file.file), - file.hash, - file.hashFormat || hashFormat, - file.alias, - file.metafile ?? false, - file.preserve ?? false, - ); - }, - ), - }; +export async function parsePackwiz(filePath: string): Promise { + const packwizFile = new Resource(filePath); + const parsedPackwizFile = toml.parse(await packwizFile.fetchContents()); + + const indexFile = packwizFile.parent.join(parsedPackwizFile.index.file); + const parsedIndexFile = toml.parse(await indexFile.fetchContents()); + const indexHashFormat = isValidHashFormat(parsedIndexFile["hash-format"]); + + return new Packwiz( + packwizFile, + { + location: indexFile, + hash: parsedPackwizFile.index.hash, + hashFormat: indexHashFormat, + files: parsedIndexFile.files.map( + (file: { + file: string; + hash: string; + hashFormat: HashFormat; + alias?: string; + metafile?: boolean; + preserve?: boolean; + }) => { + return new IndexFileEntry( + packwizFile.parent.join(file.file), + file.hash, + file.hashFormat ?? indexHashFormat, + file.alias, + file.metafile ?? false, + file.preserve ?? false, + ); + }, + ), + }, + parsedPackwizFile.name, + parsedPackwizFile["pack-format"], + parsedPackwizFile.author?.split(",").map((s: string) => s.trim()), + parsedPackwizFile.description, + parsedPackwizFile.version, + { + minecraft: parsedPackwizFile.versions.minecraft, + fabric: parsedPackwizFile.versions.fabric, + forge: parsedPackwizFile.versions.forge, + neoforge: parsedPackwizFile.versions.neoforge, + quilt: parsedPackwizFile.versions.quilt, + liteloader: parsedPackwizFile.versions.liteloader, + }, + ); }