mirror of
https://github.com/Kir-Antipov/mc-publish.git
synced 2024-11-22 08:20:58 -05:00
Implemented logic to work with dependencies
This commit is contained in:
parent
989ccf0c87
commit
002d721497
3 changed files with 556 additions and 0 deletions
57
src/dependencies/dependency.legacy.ts
Normal file
57
src/dependencies/dependency.legacy.ts
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
// TODO: Drop support for the legacy format completely.
|
||||||
|
|
||||||
|
import { FabricDependencyType } from "@/loaders/fabric/fabric-dependency-type";
|
||||||
|
import { deprecate } from "node:util";
|
||||||
|
import { DependencyInfo } from "./dependency";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the provided dependency string is in the legacy format.
|
||||||
|
*
|
||||||
|
* @param dependency - The dependency string to check.
|
||||||
|
*
|
||||||
|
* @returns A boolean indicating if the string is in the legacy format.
|
||||||
|
*/
|
||||||
|
export function isLegacyDependencyFormat(dependency: string): boolean {
|
||||||
|
return !!dependency?.includes("|") && !dependency.includes("@");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the legacy dependency format.
|
||||||
|
*
|
||||||
|
* @param dependencyFormat - The dependency string in the legacy format.
|
||||||
|
*
|
||||||
|
* @returns An object containing the parsed dependency info.
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
*
|
||||||
|
* The legacy format is: `[dependency-id] | [type]? | [version-range]?`
|
||||||
|
*/
|
||||||
|
function _parseLegacyDependencyFormat(dependencyFormat: string): DependencyInfo {
|
||||||
|
const [id, fabricType, versions] = dependencyFormat.split("|").map(x => x.trim());
|
||||||
|
const type = fabricType && FabricDependencyType.toDependencyType(FabricDependencyType.parse(fabricType));
|
||||||
|
return { id, type, versions };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the legacy dependency format with a deprecation warning.
|
||||||
|
*
|
||||||
|
* @param dependencyFormat - The dependency string in the legacy format.
|
||||||
|
*
|
||||||
|
* @returns An object containing the parsed dependency info.
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
*
|
||||||
|
* The legacy format is: `[dependency-id] | [type]? | [version-range]?`
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
*
|
||||||
|
* The old dependency string format is deprecated. Please use the new format.
|
||||||
|
*
|
||||||
|
* Example: `foo@1.0.0-2.0.0(required){modrinth:foo-fabric}#(ignore:curseforge)`.
|
||||||
|
*/
|
||||||
|
export const parseLegacyDependencyFormat = deprecate(
|
||||||
|
_parseLegacyDependencyFormat,
|
||||||
|
"The old dependency string format is deprecated. " +
|
||||||
|
"Please use the new format. " +
|
||||||
|
"Example: foo@1.0.0-2.0.0(required){modrinth:foo-fabric}#(ignore:curseforge)",
|
||||||
|
);
|
225
src/dependencies/dependency.ts
Normal file
225
src/dependencies/dependency.ts
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
import { PlatformType } from "@/platforms/platform-type";
|
||||||
|
import { $i, isIterable } from "@/utils/collections";
|
||||||
|
import { VersionRange, anyVersionRange } from "@/utils/versioning";
|
||||||
|
import { DependencyType } from "./dependency-type";
|
||||||
|
import { isLegacyDependencyFormat, parseLegacyDependencyFormat } from "./dependency.legacy";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a dependency.
|
||||||
|
*/
|
||||||
|
export interface Dependency {
|
||||||
|
/**
|
||||||
|
* The unique identifier of the dependency.
|
||||||
|
*/
|
||||||
|
get id(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The importance of the dependency for the project.
|
||||||
|
*/
|
||||||
|
get type(): DependencyType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The range of allowed versions for the dependency.
|
||||||
|
*/
|
||||||
|
get versions(): string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the dependency is ignored for a specific platform or globally
|
||||||
|
* if no platform is specified.
|
||||||
|
*
|
||||||
|
* @param platform - The platform to check for (optional).
|
||||||
|
*
|
||||||
|
* @returns A boolean indicating if the dependency is ignored.
|
||||||
|
*/
|
||||||
|
isIgnored(platform?: PlatformType): boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the project ID for the dependency on a specific platform.
|
||||||
|
*
|
||||||
|
* Useful when a dependency has different identifiers across platforms.
|
||||||
|
*
|
||||||
|
* @param platform - The platform to get the project ID for.
|
||||||
|
*
|
||||||
|
* @returns The project ID associated with the dependency on the specified platform.
|
||||||
|
*/
|
||||||
|
getProjectId(platform: PlatformType): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an intermediate representation of a dependency
|
||||||
|
* when parsing and creating {@link Dependency} objects from various formats.
|
||||||
|
*/
|
||||||
|
export interface DependencyInfo {
|
||||||
|
/**
|
||||||
|
* The unique identifier of the dependency.
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The importance of the dependency for the project.
|
||||||
|
*/
|
||||||
|
type?: string | DependencyType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The range of allowed versions for the dependency.
|
||||||
|
*/
|
||||||
|
versions?: string | string[] | VersionRange;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean indicating if the dependency is ignored globally.
|
||||||
|
*/
|
||||||
|
ignore?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of platforms the dependency is ignored on.
|
||||||
|
*/
|
||||||
|
ignoredPlatforms?: Iterable<string | PlatformType>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of aliases for the dependency on different platforms.
|
||||||
|
*/
|
||||||
|
aliases?: Iterable<readonly [string | PlatformType, string]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Representing different ways a dependency can be expressed.
|
||||||
|
*/
|
||||||
|
export type DependencyLike = Dependency | DependencyInfo | string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a dependency string and returns a Dependency object.
|
||||||
|
*
|
||||||
|
* @param dependency - The dependency string to parse.
|
||||||
|
*
|
||||||
|
* @returns A {@link Dependency} object, or `undefined` if the string is invalid.
|
||||||
|
*/
|
||||||
|
export function parseDependency(dependency: string): Dependency | undefined {
|
||||||
|
const dependencyInfo = isLegacyDependencyFormat(dependency)
|
||||||
|
? parseLegacyDependencyFormat(dependency)
|
||||||
|
: parseDependencyFormat(dependency);
|
||||||
|
|
||||||
|
return dependencyInfo && createDependency(dependencyInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A regex pattern for matching formatted dependency strings.
|
||||||
|
*/
|
||||||
|
const DEPENDENCY_REGEX = /^\s*(?<id>[^@{(#]+)(@(?<versionRange>[^@{(#]*))?(?:\((?<type>[^@{(#]*)\))?(?<aliases>(?:\{[^:=]+(?:=|:)[^}]*\})+)?(?<ignore>#\(ignore(?::(?<ignoredPlatforms>[^\)]*))?\))?\s*$/;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A regex pattern for matching dependency aliases in dependency strings.
|
||||||
|
*/
|
||||||
|
const DEPENDENCY_ALIASES_REGEX = /\{(?<platform>[^:=]+)(?:=|:)(?<id>[^}]*)\}/g;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a dependency string and returns an intermediate representation of a dependency.
|
||||||
|
*
|
||||||
|
* @param dependencyFormat - The dependency string to parse.
|
||||||
|
*
|
||||||
|
* @returns A dependency info, or `undefined` if the string is invalid.
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
*
|
||||||
|
* The format is `[dependency-id]@[version-range]?([type])?{[platform]:[dependency-id]}?#(ignore:[platform1,platform2])?`.
|
||||||
|
*/
|
||||||
|
function parseDependencyFormat(dependencyFormat: string): DependencyInfo | undefined {
|
||||||
|
const match = dependencyFormat?.match(DEPENDENCY_REGEX);
|
||||||
|
if (!match) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = match.groups.id.trim();
|
||||||
|
const versions = match.groups.versionRange?.trim();
|
||||||
|
const type = match.groups.type?.trim();
|
||||||
|
const aliases = $i(match.groups.aliases?.matchAll(DEPENDENCY_ALIASES_REGEX) || []).map(x => [x.groups.platform.trim(), x.groups.id.trim()] as const);
|
||||||
|
const ignoredPlatforms = match.groups.ignoredPlatforms?.split(",").map(x => x.trim());
|
||||||
|
const ignore = ignoredPlatforms?.length ? undefined : !!match.groups.ignore;
|
||||||
|
|
||||||
|
return { id, versions, type, aliases, ignore, ignoredPlatforms };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a dependency from the given dependency-like value.
|
||||||
|
*
|
||||||
|
* @param dependency - A dependency-like value to create a dependency from.
|
||||||
|
*
|
||||||
|
* @returns A {@link Dependency}, or `undefined` if the input is invalid.
|
||||||
|
*/
|
||||||
|
export function createDependency(dependency: DependencyLike): Dependency | undefined {
|
||||||
|
if (typeof dependency === "string") {
|
||||||
|
return parseDependency(dependency);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDependency(dependency)) {
|
||||||
|
return dependency;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dependency?.id) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = dependency.id || "";
|
||||||
|
const type = dependency.type && DependencyType.parse(dependency.type) || DependencyType.REQUIRED;
|
||||||
|
|
||||||
|
const versionRanges = typeof dependency.versions === "string"
|
||||||
|
? [dependency.versions]
|
||||||
|
: isIterable(dependency.versions)
|
||||||
|
? [...dependency.versions]
|
||||||
|
: [(dependency.versions || anyVersionRange()).toString()];
|
||||||
|
|
||||||
|
const versions = versionRanges.filter(x => x && x !== anyVersionRange().toString());
|
||||||
|
if (!versions.length) {
|
||||||
|
versions.push(anyVersionRange().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
const ignoredPlatforms = $i(dependency.ignoredPlatforms || []).map(x => PlatformType.parse(x)).filter(x => x).toSet();
|
||||||
|
const isIgnored = dependency.ignore
|
||||||
|
? () => true
|
||||||
|
: (p: PlatformType) => p ? ignoredPlatforms.has(p) : ignoredPlatforms.size === PlatformType.size;
|
||||||
|
|
||||||
|
const aliases = $i(dependency.aliases || []).map(([key, value]) => [PlatformType.parse(key), value] as const).filter(([key]) => key).toMap();
|
||||||
|
const getProjectId = (p: PlatformType) => aliases.get(p) ?? id;
|
||||||
|
|
||||||
|
return { id, versions, type, isIgnored, getProjectId };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a dependency as a string.
|
||||||
|
*
|
||||||
|
* @param dependency - The dependency to format.
|
||||||
|
*
|
||||||
|
* @returns A string representation of the dependency.
|
||||||
|
*/
|
||||||
|
export function formatDependency(dependency: Dependency): string {
|
||||||
|
if (!dependency) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const versionRange = dependency.versions.join(" || ");
|
||||||
|
const version = versionRange && versionRange !== anyVersionRange().toString() ? `@${versionRange}` : "";
|
||||||
|
|
||||||
|
const ignoredBy = $i(PlatformType.values()).filter(x => dependency.isIgnored(x)).join(",");
|
||||||
|
const ignore = ignoredBy && `#(ignore:${ignoredBy})`;
|
||||||
|
|
||||||
|
const aliases = $i(PlatformType.values()).filter(x => dependency.getProjectId(x) !== dependency.id).map(x => `{${x}:${dependency.getProjectId(x)}}`).join("");
|
||||||
|
|
||||||
|
return `${dependency.id}${version}(${dependency.type})${aliases}${ignore}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the given value is a {@link Dependency}.
|
||||||
|
*
|
||||||
|
* @param dependency - The value to check.
|
||||||
|
*
|
||||||
|
* @returns A boolean indicating if the value is a {@link Dependency}.
|
||||||
|
*/
|
||||||
|
export function isDependency(dependency: unknown): dependency is Dependency {
|
||||||
|
const d = dependency as Dependency;
|
||||||
|
return (
|
||||||
|
typeof d?.id === "string" &&
|
||||||
|
typeof d.type === DependencyType.underlyingType &&
|
||||||
|
Array.isArray(d.versions) &&
|
||||||
|
typeof d.getProjectId === "function" &&
|
||||||
|
typeof d.isIgnored === "function"
|
||||||
|
);
|
||||||
|
}
|
274
tests/unit/dependencies/dependency.spec.ts
Normal file
274
tests/unit/dependencies/dependency.spec.ts
Normal file
|
@ -0,0 +1,274 @@
|
||||||
|
import { DependencyType } from "@/dependencies/dependency-type";
|
||||||
|
import { PlatformType } from "@/platforms/platform-type";
|
||||||
|
import { parseVersionRange } from "@/utils/versioning/version-range";
|
||||||
|
import { createDependency, formatDependency, isDependency, parseDependency } from "@/dependencies/dependency";
|
||||||
|
|
||||||
|
describe("isDependency", () => {
|
||||||
|
test("returns true for Dependency-like objects", () => {
|
||||||
|
expect(isDependency(parseDependency("id"))).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false for non-Dependency-like objects", () => {
|
||||||
|
expect(isDependency({})).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false for null and undefined", () => {
|
||||||
|
expect(isDependency(null)).toBe(false);
|
||||||
|
expect(isDependency(undefined)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("parseDependency", () => {
|
||||||
|
test("parses a fully-formed dependency string", () => {
|
||||||
|
const dependency = parseDependency("id@1.0.0-2.0.0-alpha.1(optional){modrinth:modrinth-slug}{curseforge:curseforge-slug}#(ignore:curseforge,github)");
|
||||||
|
|
||||||
|
expect(dependency).toBeDefined();
|
||||||
|
expect(dependency.id).toBe("id");
|
||||||
|
expect(dependency.versions).toEqual(["1.0.0-2.0.0-alpha.1"]);
|
||||||
|
expect(dependency.type).toBe(DependencyType.OPTIONAL);
|
||||||
|
expect(dependency.getProjectId(PlatformType.MODRINTH)).toBe("modrinth-slug");
|
||||||
|
expect(dependency.getProjectId(PlatformType.CURSEFORGE)).toBe("curseforge-slug");
|
||||||
|
expect(dependency.getProjectId(PlatformType.GITHUB)).toBe("id");
|
||||||
|
expect(dependency.isIgnored(PlatformType.MODRINTH)).toBe(false);
|
||||||
|
expect(dependency.isIgnored(PlatformType.CURSEFORGE)).toBe(true);
|
||||||
|
expect(dependency.isIgnored(PlatformType.GITHUB)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("parses a dependency string with omitted 'ignore' part", () => {
|
||||||
|
const dependency = parseDependency("id@1.0.0-2.0.0-alpha.1(optional){modrinth:modrinth-slug}{curseforge:curseforge-slug}");
|
||||||
|
|
||||||
|
expect(dependency).toBeDefined();
|
||||||
|
expect(dependency.id).toBe("id");
|
||||||
|
expect(dependency.versions).toEqual(["1.0.0-2.0.0-alpha.1"]);
|
||||||
|
expect(dependency.type).toBe(DependencyType.OPTIONAL);
|
||||||
|
expect(dependency.getProjectId(PlatformType.MODRINTH)).toBe("modrinth-slug");
|
||||||
|
expect(dependency.getProjectId(PlatformType.CURSEFORGE)).toBe("curseforge-slug");
|
||||||
|
expect(dependency.getProjectId(PlatformType.GITHUB)).toBe("id");
|
||||||
|
expect(dependency.isIgnored(PlatformType.MODRINTH)).toBe(false);
|
||||||
|
expect(dependency.isIgnored(PlatformType.CURSEFORGE)).toBe(false);
|
||||||
|
expect(dependency.isIgnored(PlatformType.GITHUB)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("parses a dependency string with omitted 'aliases' part", () => {
|
||||||
|
const dependency = parseDependency("id@1.0.0-2.0.0-alpha.1(optional)#(ignore:curseforge,github)");
|
||||||
|
|
||||||
|
expect(dependency).toBeDefined();
|
||||||
|
expect(dependency.id).toBe("id");
|
||||||
|
expect(dependency.versions).toEqual(["1.0.0-2.0.0-alpha.1"]);
|
||||||
|
expect(dependency.type).toBe(DependencyType.OPTIONAL);
|
||||||
|
expect(dependency.getProjectId(PlatformType.MODRINTH)).toBe("id");
|
||||||
|
expect(dependency.getProjectId(PlatformType.CURSEFORGE)).toBe("id");
|
||||||
|
expect(dependency.getProjectId(PlatformType.GITHUB)).toBe("id");
|
||||||
|
expect(dependency.isIgnored(PlatformType.MODRINTH)).toBe(false);
|
||||||
|
expect(dependency.isIgnored(PlatformType.CURSEFORGE)).toBe(true);
|
||||||
|
expect(dependency.isIgnored(PlatformType.GITHUB)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("parses a dependency string with omitted 'type' part", () => {
|
||||||
|
const dependency = parseDependency("id@1.0.0-2.0.0-alpha.1{modrinth:modrinth-slug}{curseforge:curseforge-slug}#(ignore:curseforge,github)");
|
||||||
|
|
||||||
|
expect(dependency).toBeDefined();
|
||||||
|
expect(dependency.id).toBe("id");
|
||||||
|
expect(dependency.versions).toEqual(["1.0.0-2.0.0-alpha.1"]);
|
||||||
|
expect(dependency.type).toBe(DependencyType.REQUIRED);
|
||||||
|
expect(dependency.getProjectId(PlatformType.MODRINTH)).toBe("modrinth-slug");
|
||||||
|
expect(dependency.getProjectId(PlatformType.CURSEFORGE)).toBe("curseforge-slug");
|
||||||
|
expect(dependency.getProjectId(PlatformType.GITHUB)).toBe("id");
|
||||||
|
expect(dependency.isIgnored(PlatformType.MODRINTH)).toBe(false);
|
||||||
|
expect(dependency.isIgnored(PlatformType.CURSEFORGE)).toBe(true);
|
||||||
|
expect(dependency.isIgnored(PlatformType.GITHUB)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("parses a dependency string with omitted 'version' part", () => {
|
||||||
|
const dependency = parseDependency("id(optional){modrinth:modrinth-slug}{curseforge:curseforge-slug}#(ignore:curseforge,github)");
|
||||||
|
|
||||||
|
expect(dependency).toBeDefined();
|
||||||
|
expect(dependency.id).toBe("id");
|
||||||
|
expect(dependency.versions).toEqual(["*"]);
|
||||||
|
expect(dependency.type).toBe(DependencyType.OPTIONAL);
|
||||||
|
expect(dependency.getProjectId(PlatformType.MODRINTH)).toBe("modrinth-slug");
|
||||||
|
expect(dependency.getProjectId(PlatformType.CURSEFORGE)).toBe("curseforge-slug");
|
||||||
|
expect(dependency.getProjectId(PlatformType.GITHUB)).toBe("id");
|
||||||
|
expect(dependency.isIgnored(PlatformType.MODRINTH)).toBe(false);
|
||||||
|
expect(dependency.isIgnored(PlatformType.CURSEFORGE)).toBe(true);
|
||||||
|
expect(dependency.isIgnored(PlatformType.GITHUB)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("parses a dependency string that only consists of id", () => {
|
||||||
|
const dependency = parseDependency("id");
|
||||||
|
|
||||||
|
expect(dependency).toBeDefined();
|
||||||
|
expect(dependency.id).toBe("id");
|
||||||
|
expect(dependency.versions).toEqual(["*"]);
|
||||||
|
expect(dependency.type).toBe(DependencyType.REQUIRED);
|
||||||
|
expect(dependency.getProjectId(PlatformType.MODRINTH)).toBe("id");
|
||||||
|
expect(dependency.getProjectId(PlatformType.CURSEFORGE)).toBe("id");
|
||||||
|
expect(dependency.getProjectId(PlatformType.GITHUB)).toBe("id");
|
||||||
|
expect(dependency.isIgnored(PlatformType.MODRINTH)).toBe(false);
|
||||||
|
expect(dependency.isIgnored(PlatformType.CURSEFORGE)).toBe(false);
|
||||||
|
expect(dependency.isIgnored(PlatformType.GITHUB)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("parses a dependency string that consists of id and version", () => {
|
||||||
|
const dependency = parseDependency("id@1.0.0");
|
||||||
|
|
||||||
|
expect(dependency).toBeDefined();
|
||||||
|
expect(dependency.id).toBe("id");
|
||||||
|
expect(dependency.versions).toEqual(["1.0.0"]);
|
||||||
|
expect(dependency.type).toBe(DependencyType.REQUIRED);
|
||||||
|
expect(dependency.getProjectId(PlatformType.MODRINTH)).toBe("id");
|
||||||
|
expect(dependency.getProjectId(PlatformType.CURSEFORGE)).toBe("id");
|
||||||
|
expect(dependency.getProjectId(PlatformType.GITHUB)).toBe("id");
|
||||||
|
expect(dependency.isIgnored(PlatformType.MODRINTH)).toBe(false);
|
||||||
|
expect(dependency.isIgnored(PlatformType.CURSEFORGE)).toBe(false);
|
||||||
|
expect(dependency.isIgnored(PlatformType.GITHUB)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("parses a dependency string that consists of id, version, and type", () => {
|
||||||
|
const dependency = parseDependency("id@1.0.0(optional)");
|
||||||
|
|
||||||
|
expect(dependency).toBeDefined();
|
||||||
|
expect(dependency.id).toBe("id");
|
||||||
|
expect(dependency.versions).toEqual(["1.0.0"]);
|
||||||
|
expect(dependency.type).toBe(DependencyType.OPTIONAL);
|
||||||
|
expect(dependency.getProjectId(PlatformType.MODRINTH)).toBe("id");
|
||||||
|
expect(dependency.getProjectId(PlatformType.CURSEFORGE)).toBe("id");
|
||||||
|
expect(dependency.getProjectId(PlatformType.GITHUB)).toBe("id");
|
||||||
|
expect(dependency.isIgnored(PlatformType.MODRINTH)).toBe(false);
|
||||||
|
expect(dependency.isIgnored(PlatformType.CURSEFORGE)).toBe(false);
|
||||||
|
expect(dependency.isIgnored(PlatformType.GITHUB)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("parses a dependency string that consists of id and type", () => {
|
||||||
|
const dependency = parseDependency("id(optional)");
|
||||||
|
|
||||||
|
expect(dependency).toBeDefined();
|
||||||
|
expect(dependency.id).toBe("id");
|
||||||
|
expect(dependency.versions).toEqual(["*"]);
|
||||||
|
expect(dependency.type).toBe(DependencyType.OPTIONAL);
|
||||||
|
expect(dependency.getProjectId(PlatformType.MODRINTH)).toBe("id");
|
||||||
|
expect(dependency.getProjectId(PlatformType.CURSEFORGE)).toBe("id");
|
||||||
|
expect(dependency.getProjectId(PlatformType.GITHUB)).toBe("id");
|
||||||
|
expect(dependency.isIgnored(PlatformType.MODRINTH)).toBe(false);
|
||||||
|
expect(dependency.isIgnored(PlatformType.CURSEFORGE)).toBe(false);
|
||||||
|
expect(dependency.isIgnored(PlatformType.GITHUB)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("dependency type parsing is case-insensitive", () => {
|
||||||
|
for (const type of DependencyType.values()) {
|
||||||
|
expect(parseDependency(`id(${type.toLowerCase()})`)?.type).toBe(type);
|
||||||
|
expect(parseDependency(`id(${type.toUpperCase()})`)?.type).toBe(type);
|
||||||
|
expect(parseDependency(`id(${DependencyType.friendlyNameOf(type)})`)?.type).toBe(type);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("platform type parsing is case-insensitive", () => {
|
||||||
|
for (const platform of PlatformType.values()) {
|
||||||
|
expect(parseDependency(`id{${platform.toLowerCase()}:another-id}`)?.getProjectId(platform)).toBe("another-id");
|
||||||
|
expect(parseDependency(`id{${platform.toUpperCase()}:another-id}`)?.getProjectId(platform)).toBe("another-id");
|
||||||
|
expect(parseDependency(`id{${PlatformType.friendlyNameOf(platform)}:another-id}`)?.getProjectId(platform)).toBe("another-id");
|
||||||
|
|
||||||
|
expect(parseDependency(`id#(ignore:${platform.toLowerCase()})`)?.isIgnored(platform)).toBe(true);
|
||||||
|
expect(parseDependency(`id#(ignore:${platform.toUpperCase()})`)?.isIgnored(platform)).toBe(true);
|
||||||
|
expect(parseDependency(`id#(ignore:${PlatformType.friendlyNameOf(platform)})`)?.isIgnored(platform)).toBe(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns undefined if the input string is null, undefined, or empty", () => {
|
||||||
|
expect(parseDependency(undefined)).toBeUndefined();
|
||||||
|
expect(parseDependency(null)).toBeUndefined();
|
||||||
|
expect(parseDependency("")).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("cerateDependency", () => {
|
||||||
|
test("parses dependency strings", () => {
|
||||||
|
expect(createDependency("id")).toBeDefined();
|
||||||
|
expect(createDependency("id@1.0.0")).toBeDefined();
|
||||||
|
expect(createDependency("id@1.0.0(optional)")).toBeDefined();
|
||||||
|
expect(createDependency("id(optional)")).toBeDefined();
|
||||||
|
expect(createDependency("id@1.0.0-2.0.0-alpha.1(optional){modrinth:modrinth-slug}{curseforge:curseforge-slug}#(ignore:curseforge,github)")).toBeDefined();
|
||||||
|
expect(createDependency("id@1.0.0-2.0.0-alpha.1(optional){modrinth:modrinth-slug}{curseforge:curseforge-slug}")).toBeDefined();
|
||||||
|
expect(createDependency("id@1.0.0-2.0.0-alpha.1(optional)#(ignore:curseforge,github)")).toBeDefined();
|
||||||
|
expect(createDependency("id@1.0.0-2.0.0-alpha.1{modrinth:modrinth-slug}{curseforge:curseforge-slug}#(ignore:curseforge,github)")).toBeDefined();
|
||||||
|
expect(createDependency("id(optional){modrinth:modrinth-slug}{curseforge:curseforge-slug}#(ignore:curseforge,github)")).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("converts DependencyInfo-like objects to Dependency objects", () => {
|
||||||
|
expect(createDependency({ id: "id" })?.id).toBe("id");
|
||||||
|
expect(createDependency({ id: "id", type: DependencyType.EMBEDDED })?.type).toBe(DependencyType.EMBEDDED);
|
||||||
|
expect(createDependency({ id: "id", versions: "1.0.0" })?.versions).toEqual(["1.0.0"]);
|
||||||
|
expect(createDependency({ id: "id", versions: ["1.0.0", "2.0.0"] })?.versions).toEqual(["1.0.0", "2.0.0"]);
|
||||||
|
expect(createDependency({ id: "id", versions: parseVersionRange("1.0.0") })?.versions).toEqual(["1.0.0"]);
|
||||||
|
expect(createDependency({ id: "id", ignore: true })?.isIgnored()).toBe(true);
|
||||||
|
expect(createDependency({ id: "id", ignoredPlatforms: [PlatformType.CURSEFORGE] })?.isIgnored(PlatformType.CURSEFORGE)).toBe(true);
|
||||||
|
expect(createDependency({ id: "id", aliases: [[PlatformType.CURSEFORGE, "curseforge-slug"]] })?.getProjectId(PlatformType.CURSEFORGE)).toBe("curseforge-slug");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns Dependency-like objects as is", () => {
|
||||||
|
const dependency = parseDependency("id");
|
||||||
|
|
||||||
|
expect(createDependency(dependency)).toBe(dependency);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns undefined if the input is null, undefined, or an empty string", () => {
|
||||||
|
expect(createDependency(null)).toBeUndefined();
|
||||||
|
expect(createDependency(undefined)).toBeUndefined();
|
||||||
|
expect(createDependency("")).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("formatDependency", () => {
|
||||||
|
test("formats fine-tuned dependencies as fully qualified dependency strings", () => {
|
||||||
|
const dependencies = [
|
||||||
|
"id@1.0.0-2.0.0-alpha.1(optional){curseforge:curseforge-slug}{modrinth:modrinth-slug}#(ignore:curseforge,github)",
|
||||||
|
"id@1.0.0(embedded){modrinth:modrinth-slug}#(ignore:curseforge)",
|
||||||
|
"id@1.0.0 || 2.0.0-alpha.1(conflicting){curseforge:curseforge-slug}#(ignore:github)",
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const dependency of dependencies) {
|
||||||
|
expect(formatDependency(parseDependency(dependency))).toBe(dependency);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("formats dependencies and omits unused 'ignore' section", () => {
|
||||||
|
const dependencies = [
|
||||||
|
"id@1.0.0-2.0.0-alpha.1(optional){curseforge:curseforge-slug}{modrinth:modrinth-slug}",
|
||||||
|
"id@1.0.0(embedded){modrinth:modrinth-slug}",
|
||||||
|
"id@1.0.0 || 2.0.0-alpha.1(conflicting){curseforge:curseforge-slug}",
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const dependency of dependencies) {
|
||||||
|
expect(formatDependency(parseDependency(dependency))).toBe(dependency);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("formats dependencies and omits unused 'aliases' section", () => {
|
||||||
|
const dependencies = [
|
||||||
|
"id@1.0.0-2.0.0-alpha.1(optional)#(ignore:curseforge,github)",
|
||||||
|
"id@1.0.0(embedded)#(ignore:curseforge)",
|
||||||
|
"id@1.0.0 || 2.0.0-alpha.1(conflicting)#(ignore:github)",
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const dependency of dependencies) {
|
||||||
|
expect(formatDependency(parseDependency(dependency))).toBe(dependency);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("formats dependencies and omits unused 'version' section", () => {
|
||||||
|
const dependencies = [
|
||||||
|
"id(optional){curseforge:curseforge-slug}{modrinth:modrinth-slug}#(ignore:curseforge,github)",
|
||||||
|
"id(embedded){modrinth:modrinth-slug}#(ignore:curseforge)",
|
||||||
|
"id(conflicting){curseforge:curseforge-slug}#(ignore:github)",
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const dependency of dependencies) {
|
||||||
|
expect(formatDependency(parseDependency(dependency))).toBe(dependency);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns an empty string for invalid dependencies", () => {
|
||||||
|
expect(formatDependency(null)).toBe("");
|
||||||
|
expect(formatDependency(undefined)).toBe("");
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in a new issue