mirror of
https://github.com/Kir-Antipov/mc-publish.git
synced 2024-12-01 12:50:57 -05:00
parent
bf3f3c7c01
commit
0fcfdc07c1
9 changed files with 303 additions and 4 deletions
|
@ -27,9 +27,11 @@ export default class ModConfigDependency<TMetadata extends DependencyOptions = R
|
||||||
|
|
||||||
getProjectSlug(project: PublisherTarget): string {
|
getProjectSlug(project: PublisherTarget): string {
|
||||||
const projectName = PublisherTarget.toString(project).toLowerCase();
|
const projectName = PublisherTarget.toString(project).toLowerCase();
|
||||||
const custom = this.metadata["custom"];
|
const metadata = this.metadata;
|
||||||
const projects = this.metadata["projects"];
|
const custom = metadata["custom"];
|
||||||
|
const projects = metadata["projects"];
|
||||||
return String(
|
return String(
|
||||||
|
metadata[action.name]?.[projectName]?.slug ?? metadata[action.name]?.[projectName] ??
|
||||||
custom?.[action.name]?.[projectName]?.slug ?? custom?.[action.name]?.[projectName] ??
|
custom?.[action.name]?.[projectName]?.slug ?? custom?.[action.name]?.[projectName] ??
|
||||||
projects?.[projectName]?.slug ?? projects?.[projectName] ??
|
projects?.[projectName]?.slug ?? projects?.[projectName] ??
|
||||||
custom?.projects?.[projectName]?.slug ?? custom?.projects?.[projectName] ??
|
custom?.projects?.[projectName]?.slug ?? custom?.projects?.[projectName] ??
|
||||||
|
|
|
@ -18,9 +18,11 @@ export default abstract class ModConfig<TConfig = Record<string, unknown>> imple
|
||||||
|
|
||||||
getProjectId(project: PublisherTarget): string | undefined {
|
getProjectId(project: PublisherTarget): string | undefined {
|
||||||
const projectName = PublisherTarget.toString(project).toLowerCase();
|
const projectName = PublisherTarget.toString(project).toLowerCase();
|
||||||
const custom = this.config["custom"];
|
const config = this.config;
|
||||||
const projects = this.config["projects"];
|
const custom = config["custom"];
|
||||||
|
const projects = config["projects"];
|
||||||
const projectId = (
|
const projectId = (
|
||||||
|
config[action.name]?.[projectName]?.id ?? config[action.name]?.[projectName] ??
|
||||||
custom?.[action.name]?.[projectName]?.id ?? custom?.[action.name]?.[projectName] ??
|
custom?.[action.name]?.[projectName]?.id ?? custom?.[action.name]?.[projectName] ??
|
||||||
projects?.[projectName]?.id ?? projects?.[projectName] ??
|
projects?.[projectName]?.id ?? projects?.[projectName] ??
|
||||||
custom?.projects?.[projectName]?.id ?? custom?.projects?.[projectName]
|
custom?.projects?.[projectName]?.id ?? custom?.projects?.[projectName]
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
enum ModLoaderType {
|
enum ModLoaderType {
|
||||||
Fabric = 1,
|
Fabric = 1,
|
||||||
Forge,
|
Forge,
|
||||||
|
Quilt,
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ModLoaderType {
|
namespace ModLoaderType {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import FabricModMetadataReader from "./fabric/fabric-mod-metadata-reader";
|
import FabricModMetadataReader from "./fabric/fabric-mod-metadata-reader";
|
||||||
import ForgeModMetadataReader from "./forge/forge-mod-metadata-reader";
|
import ForgeModMetadataReader from "./forge/forge-mod-metadata-reader";
|
||||||
|
import QuiltModMetadataReader from "./quilt/quilt-mod-metadata-reader";
|
||||||
import ModLoaderType from "./mod-loader-type";
|
import ModLoaderType from "./mod-loader-type";
|
||||||
import ModMetadataReader from "./mod-metadata-reader";
|
import ModMetadataReader from "./mod-metadata-reader";
|
||||||
|
|
||||||
|
@ -12,6 +13,9 @@ export default class ModMetadataReaderFactory {
|
||||||
case ModLoaderType.Forge:
|
case ModLoaderType.Forge:
|
||||||
return new ForgeModMetadataReader();
|
return new ForgeModMetadataReader();
|
||||||
|
|
||||||
|
case ModLoaderType.Quilt:
|
||||||
|
return new QuiltModMetadataReader();
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown mod loader "${ModLoaderType.toString(loaderType)}"`);
|
throw new Error(`Unknown mod loader "${ModLoaderType.toString(loaderType)}"`);
|
||||||
}
|
}
|
||||||
|
|
17
src/metadata/quilt/quilt-mod-metadata-reader.ts
Normal file
17
src/metadata/quilt/quilt-mod-metadata-reader.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import ModMetadata from "../../metadata/mod-metadata";
|
||||||
|
import ZippedModMetadataReader from "../../metadata/zipped-mod-metadata-reader";
|
||||||
|
import QuiltModMetadata from "./quilt-mod-metadata";
|
||||||
|
|
||||||
|
export default class QuiltModMetadataReader extends ZippedModMetadataReader {
|
||||||
|
constructor() {
|
||||||
|
super("quilt.mod.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected loadConfig(buffer: Buffer): Record<string, unknown> {
|
||||||
|
return JSON.parse(buffer.toString("utf8"));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected createMetadataFromConfig(config: Record<string, unknown>): ModMetadata {
|
||||||
|
return new QuiltModMetadata(config);
|
||||||
|
}
|
||||||
|
}
|
97
src/metadata/quilt/quilt-mod-metadata.ts
Normal file
97
src/metadata/quilt/quilt-mod-metadata.ts
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
import action from "../../../package.json";
|
||||||
|
import Dependency from "../../metadata/dependency";
|
||||||
|
import DependencyKind from "../../metadata/dependency-kind";
|
||||||
|
import ModConfig from "../../metadata/mod-config";
|
||||||
|
import ModConfigDependency from "../../metadata/mod-config-dependency";
|
||||||
|
import PublisherTarget from "../../publishing/publisher-target";
|
||||||
|
|
||||||
|
function extractId(id?: string): string | null {
|
||||||
|
if (!id) {
|
||||||
|
return id ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const separatorIndex = id.indexOf(":");
|
||||||
|
if (separatorIndex !== -1) {
|
||||||
|
id = id.substring(separatorIndex + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDependencyEntries(container: any, transformer?: (x: any) => void): any[] {
|
||||||
|
if (!Array.isArray(container)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transformer) {
|
||||||
|
container = container.map(x => typeof x === "string" ? ({ id: x }) : ({ ...x }));
|
||||||
|
container.forEach(transformer);
|
||||||
|
}
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ignoredByDefault = ["minecraft", "java", "quilt_loader"];
|
||||||
|
const aliases = new Map([
|
||||||
|
["fabric", "fabric-api"],
|
||||||
|
["quilted_fabric_api", "qsl"],
|
||||||
|
]);
|
||||||
|
function createDependency(body: any): Dependency {
|
||||||
|
const id = extractId(typeof body === "string" ? body : String(body.id ?? ""));
|
||||||
|
const ignore = ignoredByDefault.includes(id);
|
||||||
|
if (id.startsWith("quilted_") || id.startsWith("quilt_")) {
|
||||||
|
aliases.set(id, "qsl");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof body === "string") {
|
||||||
|
const dependencyAliases = aliases.has(id) ? new Map(PublisherTarget.getValues().map(x => [x, aliases.get(id)])) : null;
|
||||||
|
return Dependency.create({ id, ignore, aliases: dependencyAliases });
|
||||||
|
}
|
||||||
|
|
||||||
|
const dependencyMetadata = {
|
||||||
|
ignore,
|
||||||
|
...body,
|
||||||
|
id,
|
||||||
|
version: body.version ?? String(Array.isArray(body.versions) ? body.versions[0] : body.versions || "*"),
|
||||||
|
kind: (
|
||||||
|
body.incompatible && body.unless && DependencyKind.Conflicts ||
|
||||||
|
body.incompatible && DependencyKind.Breaks ||
|
||||||
|
body.embedded && DependencyKind.Includes ||
|
||||||
|
body.optional && DependencyKind.Recommends ||
|
||||||
|
DependencyKind.Depends
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if (aliases.has(id)) {
|
||||||
|
if (!dependencyMetadata[action.name]) {
|
||||||
|
dependencyMetadata[action.name] = {};
|
||||||
|
}
|
||||||
|
for (const target of PublisherTarget.getValues()) {
|
||||||
|
const targetName = PublisherTarget.toString(target).toLowerCase();
|
||||||
|
if (typeof dependencyMetadata[action.name][targetName] !== "string") {
|
||||||
|
dependencyMetadata[action.name][targetName] = aliases.get(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ModConfigDependency(dependencyMetadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class QuiltModMetadata extends ModConfig {
|
||||||
|
public readonly id: string;
|
||||||
|
public readonly name: string;
|
||||||
|
public readonly version: string;
|
||||||
|
public readonly loaders: string[];
|
||||||
|
public readonly dependencies: Dependency[];
|
||||||
|
|
||||||
|
constructor(config: Record<string, unknown>) {
|
||||||
|
super(config);
|
||||||
|
const root = <Record<string, unknown>>this.config.quilt_loader ?? {};
|
||||||
|
this.id = String(root.id ?? "");
|
||||||
|
this.name = String(root.name ?? this.id);
|
||||||
|
this.version = String(root.version ?? "*");
|
||||||
|
this.loaders = ["quilt"];
|
||||||
|
this.dependencies = getDependencyEntries(root.depends)
|
||||||
|
.concat(getDependencyEntries(root.provides, x => x.embedded = true))
|
||||||
|
.concat(getDependencyEntries(root.breaks, x => x.incompatible = true))
|
||||||
|
.map(createDependency)
|
||||||
|
.filter((x, i, self) => self.findIndex(y => x.id === y.id && x.kind === y.kind) === i);
|
||||||
|
}
|
||||||
|
}
|
89
test/content/quilt.mod.json
Normal file
89
test/content/quilt.mod.json
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"quilt_loader": {
|
||||||
|
"group": "com.example",
|
||||||
|
"id": "example-mod",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"name": "Example Mod",
|
||||||
|
"description": "Description",
|
||||||
|
"authors": [
|
||||||
|
"Author"
|
||||||
|
],
|
||||||
|
"contact": {
|
||||||
|
"homepage": "https://github.com/",
|
||||||
|
"sources": "https://github.com/",
|
||||||
|
"issues": "https://github.com/",
|
||||||
|
"wiki": "https://github.com/"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"icon": "icon.jpg",
|
||||||
|
"intermediate_mappings": "net.fabricmc:intermediary",
|
||||||
|
"environment": "*",
|
||||||
|
"entrypoints": {
|
||||||
|
"main": [
|
||||||
|
"example.ExampleMod"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"depends": [
|
||||||
|
{
|
||||||
|
"id": "quilt_loader",
|
||||||
|
"version": ">=0.11.3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "quilt_base",
|
||||||
|
"version": ">=0.40.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "minecraft",
|
||||||
|
"version": "1.17.x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "java",
|
||||||
|
"version": ">=16"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "recommended-mod",
|
||||||
|
"version": "0.2.0",
|
||||||
|
"optional": true,
|
||||||
|
"mc-publish": {
|
||||||
|
"modrinth": "AAAA",
|
||||||
|
"ignore": true
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"curseforge": 42
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"projects": {
|
||||||
|
"github": "v0.2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"provides": [
|
||||||
|
"included:included-mod"
|
||||||
|
],
|
||||||
|
"breaks": [
|
||||||
|
"breaking-mod",
|
||||||
|
{
|
||||||
|
"id": "conflicting:conflicting-mod",
|
||||||
|
"version": "<0.40.0",
|
||||||
|
"unless": "fix-conflicting-mod"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mc-publish": {
|
||||||
|
"modrinth": "AANobbMI"
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"curseforge": 394468
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"projects": {
|
||||||
|
"github": "mc1.18-0.4.0-alpha5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mixins": [
|
||||||
|
"example-mod.mixins.json"
|
||||||
|
],
|
||||||
|
"access_widener": "example.accesswidener"
|
||||||
|
}
|
|
@ -80,6 +80,7 @@ describe("convertToCurseForgeVersions", () => {
|
||||||
loaders: {
|
loaders: {
|
||||||
fabric: 7499,
|
fabric: 7499,
|
||||||
forge: 7498,
|
forge: 7498,
|
||||||
|
quilt: 9153,
|
||||||
rift: 7500
|
rift: 7500
|
||||||
},
|
},
|
||||||
java: {
|
java: {
|
||||||
|
|
|
@ -170,6 +170,92 @@ describe("ModMetadataReader.readMetadata", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Quilt", () => {
|
||||||
|
beforeAll(() => new Promise(resolve => {
|
||||||
|
const zip = new ZipFile();
|
||||||
|
zip.addFile("./test/content/quilt.mod.json", "quilt.mod.json");
|
||||||
|
zip.end();
|
||||||
|
zip.outputStream.pipe(fs.createWriteStream("example-mod.quilt.jar")).on("close", resolve);
|
||||||
|
}));
|
||||||
|
|
||||||
|
afterAll(() => new Promise(resolve => fs.unlink("example-mod.quilt.jar", resolve)));
|
||||||
|
|
||||||
|
test("the format can be read", async () => {
|
||||||
|
const metadata = await ModMetadataReader.readMetadata("example-mod.quilt.jar");
|
||||||
|
expect(metadata).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("mod info can be read", async () => {
|
||||||
|
const metadata = await ModMetadataReader.readMetadata("example-mod.quilt.jar");
|
||||||
|
expect(metadata.id).toBe("example-mod");
|
||||||
|
expect(metadata.name).toBe("Example Mod");
|
||||||
|
expect(metadata.version).toBe("0.1.0");
|
||||||
|
expect(metadata.loaders).toMatchObject(["quilt"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("project ids can be specified in the config file", async () => {
|
||||||
|
const metadata = await ModMetadataReader.readMetadata("example-mod.quilt.jar");
|
||||||
|
expect(metadata.getProjectId(PublisherTarget.Modrinth)).toBe("AANobbMI");
|
||||||
|
expect(metadata.getProjectId(PublisherTarget.CurseForge)).toBe("394468");
|
||||||
|
expect(metadata.getProjectId(PublisherTarget.GitHub)).toBe("mc1.18-0.4.0-alpha5");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("all dependencies are read", async () => {
|
||||||
|
const metadata = await ModMetadataReader.readMetadata("example-mod.quilt.jar");
|
||||||
|
expect(metadata.dependencies).toHaveLength(8);
|
||||||
|
const dependencies = metadata.dependencies.reduce((agg, x) => { agg[x.id] = x; return agg; }, <Record<string, Dependency>>{});
|
||||||
|
expect(dependencies["quilt_loader"]?.kind).toBe(DependencyKind.Depends);
|
||||||
|
expect(dependencies["quilt_base"]?.kind).toBe(DependencyKind.Depends);
|
||||||
|
expect(dependencies["minecraft"]?.kind).toBe(DependencyKind.Depends);
|
||||||
|
expect(dependencies["java"]?.kind).toBe(DependencyKind.Depends);
|
||||||
|
expect(dependencies["recommended-mod"]?.kind).toBe(DependencyKind.Recommends);
|
||||||
|
expect(dependencies["included-mod"]?.kind).toBe(DependencyKind.Includes);
|
||||||
|
expect(dependencies["conflicting-mod"]?.kind).toBe(DependencyKind.Conflicts);
|
||||||
|
expect(dependencies["breaking-mod"]?.kind).toBe(DependencyKind.Breaks);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("dependency info can be read", async () => {
|
||||||
|
const metadata = await ModMetadataReader.readMetadata("example-mod.quilt.jar");
|
||||||
|
const conflicting = metadata.dependencies.find(x => x.id === "conflicting-mod");
|
||||||
|
expect(conflicting).toBeTruthy();
|
||||||
|
expect(conflicting.id).toBe("conflicting-mod");
|
||||||
|
expect(conflicting.kind).toBe(DependencyKind.Conflicts);
|
||||||
|
expect(conflicting.version).toBe("<0.40.0");
|
||||||
|
expect(conflicting.ignore).toBe(false);
|
||||||
|
for (const project of PublisherTarget.getValues()) {
|
||||||
|
expect(conflicting.getProjectSlug(project)).toBe(conflicting.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("custom metadata can be attached to dependency entry", async () => {
|
||||||
|
const metadata = await ModMetadataReader.readMetadata("example-mod.quilt.jar");
|
||||||
|
const recommended = metadata.dependencies.find(x => x.id === "recommended-mod");
|
||||||
|
expect(recommended).toBeTruthy();
|
||||||
|
expect(recommended.id).toBe("recommended-mod");
|
||||||
|
expect(recommended.kind).toBe(DependencyKind.Recommends);
|
||||||
|
expect(recommended.version).toBe("0.2.0");
|
||||||
|
expect(recommended.ignore).toBe(true);
|
||||||
|
expect(recommended.getProjectSlug(PublisherTarget.Modrinth)).toBe("AAAA");
|
||||||
|
expect(recommended.getProjectSlug(PublisherTarget.CurseForge)).toBe("42");
|
||||||
|
expect(recommended.getProjectSlug(PublisherTarget.GitHub)).toBe("v0.2.0");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("special case dependencies (minecraft, java and quilt_loader) are ignored by default", async () => {
|
||||||
|
const metadata = await ModMetadataReader.readMetadata("example-mod.quilt.jar");
|
||||||
|
expect(metadata.dependencies.find(x => x.id === "minecraft").ignore).toBe(true);
|
||||||
|
expect(metadata.dependencies.find(x => x.id === "java").ignore).toBe(true);
|
||||||
|
expect(metadata.dependencies.find(x => x.id === "quilt_loader").ignore).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("special case dependencies (quilted_quilt_api) are replaced with their aliases", async() => {
|
||||||
|
const metadata = await ModMetadataReader.readMetadata("example-mod.quilt.jar");
|
||||||
|
const quilt = metadata.dependencies.find(x => x.id === "quilt_base");
|
||||||
|
for (const target of PublisherTarget.getValues()) {
|
||||||
|
expect(quilt.getProjectSlug(target) === "qsl");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("unsupported mod formats", () => {
|
describe("unsupported mod formats", () => {
|
||||||
test("null is returned when the format is not supported or specified file does not exist", async () => {
|
test("null is returned when the format is not supported or specified file does not exist", async () => {
|
||||||
const metadata = await ModMetadataReader.readMetadata("example-mod.unknown.jar");
|
const metadata = await ModMetadataReader.readMetadata("example-mod.unknown.jar");
|
||||||
|
|
Loading…
Reference in a new issue