mirror of
https://github.com/Kir-Antipov/mc-publish.git
synced 2024-11-25 09:51:01 -05:00
parent
5d39b55de2
commit
9570097d5b
6 changed files with 223 additions and 120 deletions
52
README.md
52
README.md
|
@ -167,7 +167,7 @@ Can be automatically retrieved from the config file of your mod:
|
||||||
|
|
||||||
- `fabric.mod.json` (Fabric)
|
- `fabric.mod.json` (Fabric)
|
||||||
|
|
||||||
- Custom `mc-publish` field *(recommended)*:
|
- Custom `mc-publish` field:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
// ...
|
// ...
|
||||||
|
@ -179,7 +179,7 @@ Can be automatically retrieved from the config file of your mod:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- Custom [`modmanager`](https://github.com/DeathsGun/ModManager) field *(recommended)*:
|
- Custom [`modmanager`](https://github.com/DeathsGun/ModManager) field:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
// ...
|
// ...
|
||||||
|
@ -191,28 +191,6 @@ Can be automatically retrieved from the config file of your mod:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- Custom `projects` field:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
// ...
|
|
||||||
"custom": {
|
|
||||||
"projects": {
|
|
||||||
"modrinth": "AANobbMI"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- `projects` field:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
// ...
|
|
||||||
"projects": {
|
|
||||||
"modrinth": "AANobbMI"
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- `mods.toml` (Forge)
|
- `mods.toml` (Forge)
|
||||||
|
|
||||||
- Custom `mc-publish` field *(recommended)*:
|
- Custom `mc-publish` field *(recommended)*:
|
||||||
|
@ -307,7 +285,7 @@ Can be automatically retrieved from the config file of your mod:
|
||||||
|
|
||||||
- `fabric.mod.json` (Fabric)
|
- `fabric.mod.json` (Fabric)
|
||||||
|
|
||||||
- Custom `mc-publish` field *(recommended)*:
|
- Custom `mc-publish` field:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
// ...
|
// ...
|
||||||
|
@ -319,7 +297,7 @@ Can be automatically retrieved from the config file of your mod:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- Custom [`modmanager`](https://github.com/DeathsGun/ModManager) field *(recommended)*:
|
- Custom [`modmanager`](https://github.com/DeathsGun/ModManager) field:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
// ...
|
// ...
|
||||||
|
@ -331,28 +309,6 @@ Can be automatically retrieved from the config file of your mod:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- Custom `projects` field:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
// ...
|
|
||||||
"custom": {
|
|
||||||
"projects": {
|
|
||||||
"curseforge": 394468
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- `projects` field:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
// ...
|
|
||||||
"projects": {
|
|
||||||
"curseforge": 394468
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- `mods.toml` (Forge)
|
- `mods.toml` (Forge)
|
||||||
|
|
||||||
- Custom `mc-publish` field *(recommended)*:
|
- Custom `mc-publish` field *(recommended)*:
|
||||||
|
|
57
src/metadata/fabric/fabric-mod-config.ts
Normal file
57
src/metadata/fabric/fabric-mod-config.ts
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import type DependencyKind from "../dependency-kind";
|
||||||
|
|
||||||
|
type Environment = "client" | "server" | "*";
|
||||||
|
|
||||||
|
type Person = string | { name: string; contact?: string; };
|
||||||
|
|
||||||
|
type Dependency = string | string[] | {
|
||||||
|
version?: string | string[];
|
||||||
|
versions?: string | string[];
|
||||||
|
custom?: Record<string, any>;
|
||||||
|
};
|
||||||
|
|
||||||
|
type DependencyContainer = {
|
||||||
|
// Dependency resolution
|
||||||
|
[Kind in keyof typeof DependencyKind as Lowercase<Kind>]?: Record<string, Dependency>;
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://fabricmc.net/wiki/documentation:fabric_mod_json
|
||||||
|
type FabricModConfig = {
|
||||||
|
// Mandatory fields
|
||||||
|
schemaVersion: 1;
|
||||||
|
id: string;
|
||||||
|
version: string;
|
||||||
|
|
||||||
|
// Mod loading
|
||||||
|
provides?: string;
|
||||||
|
environment?: Environment;
|
||||||
|
entrypoints?: Record<string, string[]>;
|
||||||
|
jars?: { file: string }[];
|
||||||
|
languageAdapters?: Record<string, string>;
|
||||||
|
mixins?: (string | { config: string, environment: Environment })[];
|
||||||
|
|
||||||
|
// Metadata
|
||||||
|
name?: string;
|
||||||
|
description?: string;
|
||||||
|
contact?: {
|
||||||
|
email?: string;
|
||||||
|
irc?: string;
|
||||||
|
homepage?: string;
|
||||||
|
issues?: string;
|
||||||
|
sources?: string;
|
||||||
|
[key: string]: string;
|
||||||
|
};
|
||||||
|
authors?: Person[];
|
||||||
|
contributors?: Person[];
|
||||||
|
license?: string | string[];
|
||||||
|
icon?: string | Record<string, string>;
|
||||||
|
|
||||||
|
// Custom fields
|
||||||
|
custom?: Record<string, any>;
|
||||||
|
} & DependencyContainer;
|
||||||
|
|
||||||
|
namespace FabricModConfig {
|
||||||
|
export const FILENAME = "fabric.mod.json";
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FabricModConfig;
|
|
@ -1,17 +1,20 @@
|
||||||
import ModMetadata from "../../metadata/mod-metadata";
|
import ModMetadata from "../mod-metadata";
|
||||||
import ZippedModMetadataReader from "../../metadata/zipped-mod-metadata-reader";
|
import ZippedModMetadataReader from "../zipped-mod-metadata-reader";
|
||||||
|
import FabricModConfig from "./fabric-mod-config";
|
||||||
import FabricModMetadata from "./fabric-mod-metadata";
|
import FabricModMetadata from "./fabric-mod-metadata";
|
||||||
|
|
||||||
export default class FabricModMetadataReader extends ZippedModMetadataReader {
|
class FabricModMetadataReader extends ZippedModMetadataReader<FabricModConfig> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("fabric.mod.json");
|
super(FabricModConfig.FILENAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected loadConfig(buffer: Buffer): Record<string, unknown> {
|
protected loadConfig(buffer: Buffer): FabricModConfig {
|
||||||
return JSON.parse(buffer.toString("utf8"));
|
return JSON.parse(buffer.toString("utf8"));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected createMetadataFromConfig(config: Record<string, unknown>): ModMetadata {
|
protected createMetadataFromConfig(config: FabricModConfig): ModMetadata {
|
||||||
return new FabricModMetadata(config);
|
return new FabricModMetadata(config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default FabricModMetadataReader;
|
||||||
|
|
|
@ -1,76 +1,158 @@
|
||||||
import action from "../../../package.json";
|
import action from "../../../package.json";
|
||||||
import ModConfig from "../../metadata/mod-config";
|
import Dependency from "../dependency";
|
||||||
import ModConfigDependency from "../../metadata/mod-config-dependency";
|
import DependencyKind from "../dependency-kind";
|
||||||
import Dependency from "../../metadata/dependency";
|
|
||||||
import DependencyKind from "../../metadata/dependency-kind";
|
|
||||||
import PublisherTarget from "../../publishing/publisher-target";
|
import PublisherTarget from "../../publishing/publisher-target";
|
||||||
|
import FabricModConfig from "./fabric-mod-config";
|
||||||
|
import ModMetadata from "../mod-metadata";
|
||||||
|
|
||||||
const ignoredByDefault = ["minecraft", "java", "fabricloader"];
|
type Aliases = Map<PublisherTarget, string>;
|
||||||
const aliases = new Map([
|
|
||||||
["fabric", "fabric-api"]
|
type FabricDependency = FabricModConfig["depends"][string];
|
||||||
]);
|
|
||||||
function getDependenciesByKind(config: any, kind: DependencyKind): Dependency[] {
|
function getDependencies(config: FabricModConfig): Dependency[] {
|
||||||
const kindName = DependencyKind.toString(kind).toLowerCase();
|
return DependencyKind.getValues().flatMap(x => getDependenciesByKind(config, x));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDependenciesByKind(config: FabricModConfig, kind: DependencyKind): Dependency[] {
|
||||||
|
const kindName = DependencyKind.toString(kind).toLowerCase() as Lowercase<keyof typeof DependencyKind>;
|
||||||
const dependencies = new Array<Dependency>();
|
const dependencies = new Array<Dependency>();
|
||||||
for (const [id, value] of Object.entries(config[kindName] || {})) {
|
for (const [id, value] of Object.entries(config[kindName] || {})) {
|
||||||
const ignore = ignoredByDefault.includes(id);
|
const ignore = isDependencyIgnoredByDefault(id);
|
||||||
if (typeof value === "string") {
|
const aliases = getDefaultDependencyAliases(id);
|
||||||
const dependencyAliases = aliases.has(id) ? new Map(PublisherTarget.getValues().map(x => [x, aliases.get(id)])) : null;
|
const dependency = parseDependency(id, kind, value, ignore, aliases, config);
|
||||||
dependencies.push(Dependency.create({ id, kind, version: value, ignore, aliases: dependencyAliases }));
|
|
||||||
} else {
|
dependencies.push(dependency);
|
||||||
const dependencyMetadata = { ignore, ...<any>value, id, kind };
|
|
||||||
if (aliases.has(id)) {
|
|
||||||
if (!dependencyMetadata.custom) {
|
|
||||||
dependencyMetadata.custom = {};
|
|
||||||
}
|
|
||||||
if (!dependencyMetadata.custom[action.name]) {
|
|
||||||
dependencyMetadata.custom[action.name] = {};
|
|
||||||
}
|
|
||||||
for (const target of PublisherTarget.getValues()) {
|
|
||||||
const targetName = PublisherTarget.toString(target).toLowerCase();
|
|
||||||
if (typeof dependencyMetadata.custom[action.name][targetName] !== "string") {
|
|
||||||
dependencyMetadata.custom[action.name][targetName] = aliases.get(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dependencies.push(new ModConfigDependency(dependencyMetadata));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return dependencies;
|
return dependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLoaders(config: any): string[] {
|
function parseDependency(id: string, kind: DependencyKind, body: FabricDependency, ignore: boolean, aliases: Aliases, config: FabricModConfig): Dependency {
|
||||||
if (config[action.name]?.quilt ?? config.custom?.[action.name]?.quilt) {
|
if (typeof body === "string" || Array.isArray(body)) {
|
||||||
|
return parseSimpleDependency(id, kind, body, ignore, aliases, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
let version = body.version || body.versions || "*";
|
||||||
|
if (Array.isArray(version)) {
|
||||||
|
version = version.join(" || ");
|
||||||
|
}
|
||||||
|
|
||||||
|
kind = getDependencyKind(id, config) ?? kind;
|
||||||
|
ignore = isDependencyIgnoredInConfig(id, config) ?? body.custom?.[action.name]?.ignore ?? ignore;
|
||||||
|
aliases = new Map([ ...(aliases || []), ...(getDependencyAliases(id, config) || []) ]);
|
||||||
|
for (const target of PublisherTarget.getValues()) {
|
||||||
|
const targetName = PublisherTarget.toString(target).toLowerCase();
|
||||||
|
const alias = body.custom?.[action.name]?.[targetName];
|
||||||
|
if (alias) {
|
||||||
|
aliases.set(target, String(alias));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Dependency.create({ id, kind, version, ignore, aliases });
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseSimpleDependency(id: string, kind: DependencyKind, version: string | string[], ignore: boolean, aliases: Aliases, config: FabricModConfig): Dependency {
|
||||||
|
if (Array.isArray(version)) {
|
||||||
|
version = version.join(" || ");
|
||||||
|
}
|
||||||
|
|
||||||
|
kind = getDependencyKind(id, config) ?? kind;
|
||||||
|
ignore = isDependencyIgnoredInConfig(id, config) ?? ignore;
|
||||||
|
aliases = new Map([ ...(aliases || []), ...(getDependencyAliases(id, config) || []) ]);
|
||||||
|
return Dependency.create({ id, kind, version, ignore, aliases });
|
||||||
|
}
|
||||||
|
|
||||||
|
const ignoredByDefault = [
|
||||||
|
"minecraft",
|
||||||
|
"java",
|
||||||
|
"fabricloader",
|
||||||
|
];
|
||||||
|
function isDependencyIgnoredByDefault(id: string): boolean {
|
||||||
|
return ignoredByDefault.includes(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isDependencyIgnoredInConfig(id: string, config: FabricModConfig): boolean | null {
|
||||||
|
return config.custom?.[action.name]?.dependencies?.[id]?.ignore;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultAliases = new Map<string, string | Aliases>([
|
||||||
|
["fabric", "fabric-api"],
|
||||||
|
]);
|
||||||
|
function getDefaultDependencyAliases(id: string): Aliases | null {
|
||||||
|
if (!defaultAliases.has(id)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const aliases = defaultAliases.get(id);
|
||||||
|
if (typeof aliases !== "string") {
|
||||||
|
return new Map([...aliases]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Map(PublisherTarget.getValues().map(x => [x, aliases]));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDependencyAliases(id: string, config: FabricModConfig): Aliases | null {
|
||||||
|
const metadata = config.custom?.[action.name]?.dependencies?.[id];
|
||||||
|
if (!metadata) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const aliases = new Map() as Aliases;
|
||||||
|
for (const target of PublisherTarget.getValues()) {
|
||||||
|
const targetName = PublisherTarget.toString(target).toLowerCase();
|
||||||
|
const alias = metadata[targetName] ?? id;
|
||||||
|
aliases.set(target, String(alias));
|
||||||
|
}
|
||||||
|
return aliases;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDependencyKind(id: string, config: FabricModConfig): DependencyKind | null {
|
||||||
|
const kind = config.custom?.[action.name]?.dependencies?.[id]?.kind;
|
||||||
|
return kind ? DependencyKind.parse(kind) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLoaders(config: FabricModConfig): string[] {
|
||||||
|
if (config.custom?.[action.name]?.quilt) {
|
||||||
return ["fabric", "quilt"];
|
return ["fabric", "quilt"];
|
||||||
}
|
}
|
||||||
return ["fabric"];
|
return ["fabric"];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class FabricModMetadata extends ModConfig {
|
function getProjects(config: FabricModConfig): Map<PublisherTarget, string> {
|
||||||
public readonly id: string;
|
const projects = new Map();
|
||||||
public readonly name: string;
|
for (const target of PublisherTarget.getValues()) {
|
||||||
public readonly version: string;
|
const targetName = PublisherTarget.toString(target).toLowerCase();
|
||||||
public readonly loaders: string[];
|
const projectId = config.custom?.[action.name]?.[targetName]
|
||||||
public readonly dependencies: Dependency[];
|
?? config.custom?.modmanager?.[targetName]
|
||||||
|
?? config.custom?.projects?.[targetName];
|
||||||
|
|
||||||
constructor(config: Record<string, unknown>) {
|
if (projectId) {
|
||||||
super(config);
|
projects.set(target, String(projectId));
|
||||||
this.id = String(this.config.id ?? "");
|
}
|
||||||
this.name = String(this.config.name ?? this.id);
|
}
|
||||||
this.version = String(this.config.version ?? "*");
|
return projects;
|
||||||
this.loaders = getLoaders(this.config);
|
}
|
||||||
this.dependencies = DependencyKind.getValues().flatMap(x => getDependenciesByKind(this.config, x));
|
|
||||||
|
class FabricModMetadata implements ModMetadata {
|
||||||
|
readonly id: string;
|
||||||
|
readonly name: string;
|
||||||
|
readonly version: string;
|
||||||
|
readonly loaders: string[];
|
||||||
|
readonly dependencies: Dependency[];
|
||||||
|
|
||||||
|
private readonly _projects: Map<PublisherTarget, string>;
|
||||||
|
|
||||||
|
constructor(config: FabricModConfig) {
|
||||||
|
this.id = String(config.id ?? "");
|
||||||
|
this.name = String(config.name ?? this.id);
|
||||||
|
this.version = String(config.version ?? "*");
|
||||||
|
this.loaders = getLoaders(config);
|
||||||
|
this.dependencies = getDependencies(config);
|
||||||
|
this._projects = getProjects(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
getProjectId(project: PublisherTarget): string | undefined {
|
getProjectId(project: PublisherTarget): string | undefined {
|
||||||
const projectId = super.getProjectId(project);
|
return this._projects.get(project);
|
||||||
if (projectId) {
|
|
||||||
return projectId;
|
|
||||||
}
|
|
||||||
|
|
||||||
const projectName = PublisherTarget.toString(project).toLowerCase();
|
|
||||||
const custom = <any>this.config.custom;
|
|
||||||
const modManagerProjectId = custom?.modmanager?.[projectName]?.id ?? custom?.modmanager?.[projectName];
|
|
||||||
return modManagerProjectId === undefined ? modManagerProjectId : String(modManagerProjectId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default FabricModMetadata;
|
||||||
|
|
|
@ -23,10 +23,10 @@
|
||||||
"example-mod.mixins.json"
|
"example-mod.mixins.json"
|
||||||
],
|
],
|
||||||
|
|
||||||
"projects": {
|
|
||||||
"modrinth": "AANobbMI"
|
|
||||||
},
|
|
||||||
"custom": {
|
"custom": {
|
||||||
|
"modmanager": {
|
||||||
|
"modrinth": "AANobbMI"
|
||||||
|
},
|
||||||
"projects": {
|
"projects": {
|
||||||
"curseforge": 394468
|
"curseforge": 394468
|
||||||
},
|
},
|
||||||
|
@ -38,20 +38,19 @@
|
||||||
"depends": {
|
"depends": {
|
||||||
"fabricloader": ">=0.11.3",
|
"fabricloader": ">=0.11.3",
|
||||||
"fabric": ">=0.40.0",
|
"fabric": ">=0.40.0",
|
||||||
"minecraft": "1.17.x",
|
"minecraft": [
|
||||||
|
"1.17",
|
||||||
|
"1.17.1"
|
||||||
|
],
|
||||||
"java": ">=16"
|
"java": ">=16"
|
||||||
},
|
},
|
||||||
"recommends": {
|
"recommends": {
|
||||||
"recommended-mod": {
|
"recommended-mod": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"projects": {
|
|
||||||
"modrinth": "AAAA"
|
|
||||||
},
|
|
||||||
"custom": {
|
"custom": {
|
||||||
"projects": {
|
|
||||||
"curseforge": 42
|
|
||||||
},
|
|
||||||
"mc-publish": {
|
"mc-publish": {
|
||||||
|
"modrinth": "AAAA",
|
||||||
|
"curseforge": 42,
|
||||||
"github": "v0.2.0",
|
"github": "v0.2.0",
|
||||||
"ignore": true
|
"ignore": true
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,6 +65,12 @@ describe("ModMetadataReader.readMetadata", () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("version array is supported", async () => {
|
||||||
|
const metadata = (await ModMetadataReader.readMetadata("example-mod.fabric.jar"))!;
|
||||||
|
const minecraft = metadata.dependencies.find(x => x.id === "minecraft");
|
||||||
|
expect(minecraft.version).toStrictEqual("1.17 || 1.17.1");
|
||||||
|
});
|
||||||
|
|
||||||
test("custom metadata can be attached to dependency entry", async () => {
|
test("custom metadata can be attached to dependency entry", async () => {
|
||||||
const metadata = (await ModMetadataReader.readMetadata("example-mod.fabric.jar"))!;
|
const metadata = (await ModMetadataReader.readMetadata("example-mod.fabric.jar"))!;
|
||||||
const recommended = metadata.dependencies.find(x => x.id === "recommended-mod")!;
|
const recommended = metadata.dependencies.find(x => x.id === "recommended-mod")!;
|
||||||
|
|
Loading…
Reference in a new issue