add more providers and refactor path to support urls
This commit is contained in:
parent
fff43a8d8f
commit
eb836474ab
5 changed files with 175 additions and 77 deletions
3
bun.lock
3
bun.lock
|
@ -4,6 +4,7 @@
|
|||
"": {
|
||||
"name": "packwizjs",
|
||||
"dependencies": {
|
||||
"curseforge-api": "^1.3.0",
|
||||
"toml": "^3.0.0",
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -109,6 +110,8 @@
|
|||
|
||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||
|
||||
"curseforge-api": ["curseforge-api@1.3.0", "", {}, "sha512-MxIFAtmzCBmZaDUF7bDs8wPa8mAr1qneT9YKw1pVTjW0+cWGYop05Qm+ZEoAVToVQPYcNFHA2GoBPNX48zo+aQ=="],
|
||||
|
||||
"data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="],
|
||||
|
||||
"data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="],
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
"typescript": "^5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"curseforge-api": "^1.3.0",
|
||||
"toml": "^3.0.0"
|
||||
}
|
||||
}
|
||||
|
|
140
src/parser.ts
140
src/parser.ts
|
@ -1,55 +1,109 @@
|
|||
import * as fs from "fs";
|
||||
import * as toml from "toml";
|
||||
import FilePath from "./path";
|
||||
import { CurseForgeClient } from "curseforge-api";
|
||||
import Resource from "./resource";
|
||||
|
||||
// const CURSE_CLIENT = new CurseForgeClient();
|
||||
|
||||
export interface Metafile {
|
||||
name: string;
|
||||
filename: string;
|
||||
side: "server" | "client" | "both";
|
||||
provider: ModrinthProvider | CurseForgeProvider;
|
||||
provider: Provider;
|
||||
}
|
||||
|
||||
export interface ModrinthProvider {
|
||||
url: string;
|
||||
hashFormat: string;
|
||||
hash: string;
|
||||
modId: string;
|
||||
versionId: string;
|
||||
export abstract class Provider {
|
||||
constructor(
|
||||
public hash: string,
|
||||
public hashFormat: string,
|
||||
) {}
|
||||
|
||||
abstract getDownloadUrl(): Promise<Resource>;
|
||||
}
|
||||
|
||||
export interface CurseForgeProvider {
|
||||
hashFormat: string;
|
||||
hash: string;
|
||||
mode: string;
|
||||
fileId: number;
|
||||
projectId: number;
|
||||
export class UrlProvider extends Provider {
|
||||
constructor(
|
||||
public hash: string,
|
||||
public hashFormat: string,
|
||||
public url: string,
|
||||
) {
|
||||
super(hash, hashFormat);
|
||||
}
|
||||
|
||||
async getDownloadUrl(): Promise<Resource> {
|
||||
return new Resource(this.url);
|
||||
}
|
||||
}
|
||||
|
||||
export class ModrinthProvider extends UrlProvider {
|
||||
constructor(
|
||||
hash: string,
|
||||
hashFormat: string,
|
||||
url: string,
|
||||
public modId: string,
|
||||
public versionId: string,
|
||||
) {
|
||||
super(hash, hashFormat, url);
|
||||
}
|
||||
}
|
||||
|
||||
export class GitHubProvider extends UrlProvider {
|
||||
constructor(
|
||||
hash: string,
|
||||
hashFormat: string,
|
||||
url: string,
|
||||
public branch: string,
|
||||
public regex: string,
|
||||
public slug: string,
|
||||
public tag: string,
|
||||
) {
|
||||
super(hash, hashFormat, url);
|
||||
}
|
||||
}
|
||||
|
||||
export class CurseForgeProvider extends Provider {
|
||||
constructor(
|
||||
public hash: string,
|
||||
public hashFormat: string,
|
||||
public mode: string,
|
||||
public fileId: number,
|
||||
public projectId: number,
|
||||
) {
|
||||
super(hash, hashFormat);
|
||||
}
|
||||
|
||||
async getDownloadUrl(): Promise<Resource> {
|
||||
// const mod = await CURSE_CLIENT.getMod(this.projectId);
|
||||
// const file = await mod.getFile(this.fileId);
|
||||
// return new Resource(await file.getDownloadURL());
|
||||
return new Resource("https://google.com/search?q=curseforge+sucks"); // TODO: figure this out, i hate curseforge
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a file entry in the Packwiz index.
|
||||
*/
|
||||
export class IndexFileEntry {
|
||||
readonly file: FilePath;
|
||||
readonly file: Resource;
|
||||
readonly hash: string;
|
||||
readonly metafile: boolean;
|
||||
|
||||
constructor(
|
||||
file: string | FilePath,
|
||||
file: string | Resource,
|
||||
hash: string,
|
||||
metafile: boolean = false,
|
||||
) {
|
||||
this.file = file instanceof FilePath ? file : new FilePath(file);
|
||||
this.file = file instanceof Resource ? file : new Resource(file);
|
||||
this.hash = hash;
|
||||
this.metafile = metafile;
|
||||
}
|
||||
|
||||
parse(): Metafile {
|
||||
async parse(): Promise<Metafile> {
|
||||
if (this.metafile === false) {
|
||||
throw new Error(
|
||||
"Cannot parse non-metafiles! Use `file.readText()` instead to get the file contents.",
|
||||
);
|
||||
}
|
||||
const fileContent = this.file.readText();
|
||||
const fileContent = await this.file.fetchContents();
|
||||
const parsed = toml.parse(fileContent);
|
||||
|
||||
const metafile: Metafile = {
|
||||
|
@ -62,23 +116,29 @@ export class IndexFileEntry {
|
|||
return metafile;
|
||||
}
|
||||
|
||||
private parseProvider(parsed: any): ModrinthProvider | CurseForgeProvider {
|
||||
private parseProvider(parsed: any): Provider {
|
||||
if (parsed.update?.modrinth) {
|
||||
return {
|
||||
url: parsed.download.url,
|
||||
hashFormat: parsed.download["hash-format"],
|
||||
hash: parsed.download.hash,
|
||||
modId: parsed.update.modrinth["mod-id"],
|
||||
versionId: parsed.update.modrinth["version"],
|
||||
};
|
||||
return new ModrinthProvider(
|
||||
parsed.download.url,
|
||||
parsed.download["hash-format"],
|
||||
parsed.download.hash,
|
||||
parsed.update.modrinth["mod-id"],
|
||||
parsed.update.modrinth["version"],
|
||||
);
|
||||
} else if (parsed.update?.curseforge) {
|
||||
return {
|
||||
hashFormat: parsed.download["hash-format"],
|
||||
hash: parsed.download.hash,
|
||||
mode: parsed.download.mode,
|
||||
fileId: parsed.update.curseforge["file-id"],
|
||||
projectId: parsed.update.curseforge["project-id"],
|
||||
};
|
||||
return new CurseForgeProvider(
|
||||
parsed.download["hash-format"],
|
||||
parsed.download.hash,
|
||||
parsed.download.mode,
|
||||
parsed.update.curseforge["file-id"],
|
||||
parsed.update.curseforge["project-id"],
|
||||
);
|
||||
} else if (parsed.download) {
|
||||
return new UrlProvider(
|
||||
parsed.download.hash,
|
||||
parsed.download["hash-format"],
|
||||
parsed.download.url,
|
||||
);
|
||||
} else {
|
||||
throw new Error("Unknown provider in TOML.");
|
||||
}
|
||||
|
@ -89,7 +149,7 @@ export class IndexFileEntry {
|
|||
* Represents the structure of a Packwiz index file, as well as providing its location.
|
||||
*/
|
||||
export interface PackwizIndex {
|
||||
location: FilePath;
|
||||
location: Resource;
|
||||
hashFormat: string;
|
||||
files: IndexFileEntry[];
|
||||
}
|
||||
|
@ -103,9 +163,11 @@ export interface PackwizIndex {
|
|||
* @param indexFilePath - The path of the TOML file.
|
||||
* @returns The parsed index as a structured object.
|
||||
*/
|
||||
export function parsePackwizIndex(indexFilePath: string): PackwizIndex {
|
||||
const indexFile = new FilePath(indexFilePath);
|
||||
const parsed = toml.parse(indexFile.readText());
|
||||
export async function parsePackwizIndex(
|
||||
indexFilePath: string,
|
||||
): Promise<PackwizIndex> {
|
||||
const indexFile = new Resource(indexFilePath);
|
||||
const parsed = toml.parse(await indexFile.fetchContents());
|
||||
return {
|
||||
location: indexFile,
|
||||
hashFormat: parsed["hash-format"],
|
||||
|
|
38
src/path.ts
38
src/path.ts
|
@ -1,38 +0,0 @@
|
|||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
export default class FilePath {
|
||||
constructor(public readonly path: string) {}
|
||||
|
||||
toString() {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
get name() {
|
||||
return path.basename(this.path);
|
||||
}
|
||||
|
||||
get parent() {
|
||||
return new FilePath(path.dirname(this.path));
|
||||
}
|
||||
|
||||
get ext() {
|
||||
return path.extname(this.path);
|
||||
}
|
||||
|
||||
exists() {
|
||||
return fs.existsSync(this.path);
|
||||
}
|
||||
|
||||
readText(): string {
|
||||
return fs.readFileSync(this.path, "utf-8");
|
||||
}
|
||||
|
||||
// writeText(content: string) {
|
||||
// fs.writeFileSync(this.path, content, "utf-8");
|
||||
// }
|
||||
|
||||
join(...segments: string[]) {
|
||||
return new FilePath(path.join(this.path, ...segments));
|
||||
}
|
||||
}
|
70
src/resource.ts
Normal file
70
src/resource.ts
Normal file
|
@ -0,0 +1,70 @@
|
|||
import * as fs from "fs/promises";
|
||||
import { URL } from "url";
|
||||
import * as path from "path";
|
||||
|
||||
export default class Resource {
|
||||
constructor(public readonly path: string) {}
|
||||
|
||||
toString() {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
get isUrl() {
|
||||
try {
|
||||
new URL(this.path);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this.isUrl
|
||||
? new URL(this.path).pathname.split("/").pop() || ""
|
||||
: path.basename(this.path);
|
||||
}
|
||||
|
||||
get parent() {
|
||||
if (this.isUrl) {
|
||||
const url = new URL(this.path);
|
||||
url.pathname = path.dirname(url.pathname);
|
||||
return new Resource(url.toString());
|
||||
}
|
||||
return new Resource(path.dirname(this.path));
|
||||
}
|
||||
|
||||
get ext() {
|
||||
return this.isUrl
|
||||
? path.extname(new URL(this.path).pathname)
|
||||
: path.extname(this.path);
|
||||
}
|
||||
|
||||
async exists(): Promise<boolean> {
|
||||
if (this.isUrl) {
|
||||
const response = await fetch(this.path);
|
||||
if (!response.ok) return false;
|
||||
return true;
|
||||
} else {
|
||||
return fs.exists(this.path);
|
||||
}
|
||||
}
|
||||
|
||||
async fetchContents(): Promise<string> {
|
||||
if (this.isUrl) {
|
||||
const response = await fetch(this.path);
|
||||
if (!response.ok)
|
||||
throw new Error(`Failed to fetch ${this.path}: ${response.statusText}`);
|
||||
return await response.text();
|
||||
}
|
||||
return fs.readFile(this.path, { encoding: "utf8" });
|
||||
}
|
||||
|
||||
join(...segments: string[]) {
|
||||
if (this.isUrl) {
|
||||
const url = new URL(this.path);
|
||||
url.pathname = path.join(url.pathname, ...segments);
|
||||
return new Resource(url.toString());
|
||||
}
|
||||
return new Resource(path.join(this.path, ...segments));
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue