Externalize spec typings, use eslint.

Tslint is deprecated, as such we have moved to eslint. Linted the project with more
stringent rules. The configuration will be changed as we figure out which rules we
should keep.
This commit is contained in:
Daniel Scalzi
2020-01-24 19:27:20 -05:00
parent 8911f54039
commit 064a664687
40 changed files with 2121 additions and 493 deletions

View File

@@ -12,11 +12,11 @@ import { VersionUtil } from './util/versionutil'
dotenv.config()
function getRoot() {
function getRoot(): string {
return resolvePath(process.env.ROOT as string)
}
function getBaseURL() {
function getBaseURL(): string {
let baseUrl = process.env.BASE_URL as string
// Users must provide protocol in all other instances.
if (baseUrl.indexOf('//') === -1) {
@@ -63,6 +63,7 @@ function getBaseURL() {
// })
// }
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
function namePositional(yargs: yargs.Argv) {
return yargs.option('name', {
describe: 'Distribution index file name.',
@@ -101,7 +102,7 @@ const initCommand: yargs.CommandModule = {
describe: 'Base init command.',
builder: (yargs) => {
return yargs
.command(initRootCommand)
.command(initRootCommand)
},
handler: (argv) => {
argv._handled = true
@@ -117,32 +118,32 @@ const generateServerCommand: yargs.CommandModule = {
builder: (yargs) => {
// yargs = rootOption(yargs)
return yargs
.positional('id', {
describe: 'Server id.',
type: 'string'
})
.positional('version', {
describe: 'Minecraft version.',
type: 'string'
})
.option('forge', {
describe: 'Forge version.',
type: 'string',
default: null
})
.option('liteloader', {
describe: 'LiteLoader version.',
type: 'string',
default: null
})
.positional('id', {
describe: 'Server id.',
type: 'string'
})
.positional('version', {
describe: 'Minecraft version.',
type: 'string'
})
.option('forge', {
describe: 'Forge version.',
type: 'string',
default: null
})
.option('liteloader', {
describe: 'LiteLoader version.',
type: 'string',
default: null
})
},
handler: async (argv) => {
argv.root = getRoot()
console.debug(`Root set to ${argv.root}`)
console.debug(`Generating server ${argv.id} for Minecraft ${argv.version}.`,
`\n\t├ Forge version: ${argv.forge}`,
`\n\t└ LiteLoader version: ${argv.liteloader}`)
`\n\t├ Forge version: ${argv.forge}`,
`\n\t└ LiteLoader version: ${argv.liteloader}`)
if (VersionUtil.isPromotionVersion(argv.forge as string)) {
console.debug(`Resolving ${argv.forge} Forge Version..`)
@@ -197,8 +198,8 @@ const generateCommand: yargs.CommandModule = {
describe: 'Base generate command.',
builder: (yargs) => {
return yargs
.command(generateServerCommand)
.command(generateDistroCommand)
.command(generateServerCommand)
.command(generateDistroCommand)
},
handler: (argv) => {
argv._handled = true
@@ -273,14 +274,14 @@ const testCommand: yargs.CommandModule = {
// Registering yargs configuration.
// tslint:disable-next-line:no-unused-expression
yargs
.version(false)
.scriptName('')
.command(initCommand)
.command(generateCommand)
.command(validateCommand)
.command(latestForgeCommand)
.command(recommendedForgeCommand)
.command(testCommand)
.demandCommand()
.help()
.argv
.version(false)
.scriptName('')
.command(initCommand)
.command(generateCommand)
.command(validateCommand)
.command(latestForgeCommand)
.command(recommendedForgeCommand)
.command(testCommand)
.demandCommand()
.help()
.argv

View File

@@ -2,7 +2,7 @@
export interface ModsToml {
modLoader: string,
modLoader: string
loaderVersion: string
issueTrackerURL?: string
@@ -19,9 +19,9 @@ export interface ModsToml {
}>
dependencies?: {[modId: string]: {
modId: string,
mandatory: boolean,
versionRange: string,
modId: string
mandatory: boolean
versionRange: string
ordering?: 'NONE' | 'BEFORE' | 'AFTER'
side: 'BOTH' | 'CLIENT' | 'SERVER'
}}

View File

@@ -6,17 +6,17 @@ export interface VersionManifest113 {
type: string
mainClass: string
inheritsFrom: string
logging: any
logging: {}
arguments: {
game: string[]
}
libraries: Array<{
name: string,
name: string
downloads: {
artifact: {
path: string,
url: string,
sha1: string,
path: string
url: string
sha1: string
size: number
}
}

View File

@@ -8,13 +8,13 @@ export interface VersionManifest17 {
mainClass: string
inheritsFrom: string
jar: string
logging: any
logging: {}
libraries: Array<{
name: string,
url?: string,
checksums?: string[],
serverreq?: boolean,
clientreq?: boolean,
name: string
url?: string
checksums?: string[]
serverreq?: boolean
clientreq?: boolean
comment?: string
}>

View File

@@ -1,25 +0,0 @@
export interface Artifact {
/**
* The size of the artifact.
*/
size: number
/**
* The MD5 hash of the artifact. This will be used to validate local artifacts.
*/
MD5: string
/**
* The artifact's download url.
*/
url: string
/**
* A relative path to where the file will be saved. This is appended to the base
* path for the module's declared type.
* If this is not specified, the path will be resolved based on the module's ID.
*/
path?: string
}

View File

@@ -1,35 +0,0 @@
import { Server } from './server'
export interface Distribution {
version: string
/**
* Global settings for Discord Rich Presence.
*/
discord?: {
/**
* Client ID for the Application registered with Discord.
*/
clientId: string,
/**
* Tootltip for the smallImageKey.
*/
smallImageText: string,
/**
* Name of the uploaded image for the small profile artwork.
*/
smallImageKey: string
}
/**
* A URL to a RSS feed. Used for loading news.
*/
rss: string
/**
* Array of server objects.
*/
servers: Server[]
}

View File

@@ -1,51 +0,0 @@
import { Artifact } from './artifact'
import { Required } from './required'
import { Type } from './type'
export interface Module {
/**
* The ID of the module. All modules that are not of type File MUST use a maven identifier.
* Version information and other metadata is pulled from the identifier. Modules which are
* stored maven style use the identifier to resolve the destination path. If the extension
* is not provided, it defaults to jar.
*
* Template
*
* my.group:arifact:version@extension
*
* my/group/artifact/version/artifact-version.extension
*
* If the module's artifact does not declare the path property, its path will be resolved from the ID.
*/
id: string
/**
* The name of the module. Used on the UI.
*/
name: string
/**
* The type of the module.
*/
type: Type
/**
* Defines whether or not the module is required. If omitted, then the module will be required.
*/
required?: Required
/**
* The download artifact for the module.
*/
artifact: Artifact
/**
* An array of sub modules declared by this module. Typically, files which require other files
* are declared as submodules. A quick example would be a mod, and the configuration file for
* that mod. Submodules can also declare submodules of their own. The file is parsed recursively,
* so there is no limit.
*/
subModules?: Module[]
}

View File

@@ -1,14 +0,0 @@
export interface Required {
/**
* If the module is required. Defaults to true if this property is omited.
*/
value?: boolean
/**
* If the module is enabled by default. Has no effect unless Required.value
* is false. Defaults to true if this property is omited.
*/
def?: boolean
}

View File

@@ -1,81 +0,0 @@
import { Module } from './module'
export interface Server {
/**
* The ID of the server. The launcher saves mod configurations and selected servers
* by ID. If the ID changes, all data related to the old ID will be wiped.
*/
id: string
/**
* The name of the server. This is what users see on the UI.
*/
name: string
/**
* A brief description of the server. Displayed on the UI to provide users more information.
*/
description: string
/**
* A URL to the server's icon. Will be displayed on the UI.
*/
icon: string
/**
* The version of the server configuration.
*/
version: string
/**
* The server's IP address.
*/
address: string
/**
* The version of minecraft that the server is running.
*/
minecraftVersion: string
/**
* Server specific settings used for Discord Rich Presence.
*/
discord?: {
/**
* Short ID for the server. Displayed on the second status line as Server: shortId.
*/
shortId: string,
/**
* Ttooltip for the largeImageKey.
*/
largeImageText: string
/**
* Name of the uploaded image for the large profile artwork.
*/
largeImageKey: string
}
/**
* Only one server in the array should have the mainServer property enabled. This
* will tell the launcher that this is the default server to select if either the
* previously selected server is invalid, or there is no previously selected server.
* If this field is not defined by any server (avoid this), the first server will
* be selected as the default. If multiple servers have mainServer enabled, the first
* one the launcher finds will be the effective value. Servers which are not the default
* may omit this property rather than explicitly setting it to false.
*/
mainServer?: boolean
/**
* Whether or not the server can be autoconnected to. If false, the server will
* not be autoconnected to even when the user has the autoconnect setting enabled.
*/
autoconnect: boolean
/**
* An array of module objects.
*/
modules: Module[]
}

View File

@@ -1,58 +0,0 @@
export enum Type {
Library = 'Library',
ForgeHosted = 'ForgeHosted',
Forge = 'Forge',
LiteLoader = 'LiteLoader',
ForgeMod = 'ForgeMod',
LiteMod = 'LiteMod',
File = 'File',
VersionManifest = 'VersionManifest'
}
export interface TypeMetadata {
id: string
defaultExtension?: string
}
export const TypeMetadata: {[property: string]: TypeMetadata} = {
Library: {
id: Type.Library,
defaultExtension: 'jar'
},
/**
* @deprecated Will be replaced by Types.Forge.
*/
ForgeHosted: {
id: Type.ForgeHosted,
defaultExtension: 'jar'
},
Forge: {
id: Type.Forge,
defaultExtension: 'jar'
},
LiteLoader: {
id: Type.LiteLoader,
defaultExtension: 'jar'
},
ForgeMod: {
id: Type.ForgeMod,
defaultExtension: 'jar'
},
LiteMod: {
id: Type.LiteMod,
defaultExtension: 'litemod'
},
File: {
id: Type.File
},
VersionManifest: {
id: Type.VersionManifest,
defaultExtension: 'json'
}
}

View File

@@ -15,7 +15,7 @@ export abstract class BaseFileStructure implements FileStructure {
this.containerDirectory = resolve(absoluteRoot, structRoot)
}
public async init() {
public async init(): Promise<void> {
mkdirs(this.containerDirectory)
}

View File

@@ -1,5 +1,5 @@
import { mkdirs } from 'fs-extra'
import { Distribution } from '../../spec/distribution'
import { Distribution } from 'helios-distribution-types'
import { ModelStructure } from './ModelStructure'
import { ServerStructure } from './server.struct'
@@ -14,7 +14,7 @@ export class DistributionStructure implements ModelStructure<Distribution> {
this.serverStruct = new ServerStructure(this.absoluteRoot, this.baseUrl)
}
public async init() {
public async init(): Promise<void> {
await mkdirs(this.absoluteRoot)
await this.serverStruct.init()
}

View File

@@ -1,7 +1,8 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { Stats } from 'fs'
import { Type } from 'helios-distribution-types'
import { join } from 'path'
import { resolve } from 'url'
import { Type } from '../../../spec/type'
import { ModuleStructure } from './module.struct'
export class MiscFileStructure extends ModuleStructure {

View File

@@ -1,8 +1,8 @@
import { Stats } from 'fs-extra'
import { Type } from 'helios-distribution-types'
import { join } from 'path'
import { resolve } from 'url'
import { VersionSegmented } from '../../../../util/VersionSegmented'
import { Type } from '../../../spec/type'
import { ModuleStructure } from './module.struct'
export abstract class BaseForgeModStructure extends ModuleStructure implements VersionSegmented {
@@ -17,9 +17,11 @@ export abstract class BaseForgeModStructure extends ModuleStructure implements V
public abstract isForVersion(version: string): boolean
// eslint-disable-next-line @typescript-eslint/no-unused-vars
protected async getModuleUrl(name: string, path: string, stats: Stats): Promise<string> {
return resolve(this.baseUrl, join(this.relativeRoot, name))
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
protected async getModulePath(name: string, path: string, stats: Stats): Promise<string | null> {
return null
}

View File

@@ -10,7 +10,7 @@ export class ForgeModStructure113 extends BaseForgeModStructure {
public static readonly IMPLEMENTATION_VERSION_REGEX = /^Implementation-Version: (.+)[\r\n]/
public static isForVersion(version: string) {
public static isForVersion(version: string): boolean {
return VersionUtil.isVersionAcceptable(version, [13, 14, 15])
}
@@ -37,8 +37,7 @@ export class ForgeModStructure113 extends BaseForgeModStructure {
}
private getForgeModMetadata(buf: Buffer, name: string): ModsToml {
if (!this.forgeModMetadata.hasOwnProperty(name)) {
if (!Object.prototype.hasOwnProperty.call(this.forgeModMetadata, name)) {
const zip = new AdmZip(buf)
const zipEntries = zip.getEntries()

View File

@@ -8,7 +8,7 @@ import { BaseForgeModStructure } from '../forgemod.struct'
export class ForgeModStructure17 extends BaseForgeModStructure {
public static isForVersion(version: string) {
public static isForVersion(version: string): boolean {
return VersionUtil.isVersionAcceptable(version, [7, 8, 9, 10, 11, 12])
}
@@ -35,7 +35,7 @@ export class ForgeModStructure17 extends BaseForgeModStructure {
}
private getForgeModMetadata(buf: Buffer, name: string): McModInfo {
if (!this.forgeModMetadata.hasOwnProperty(name)) {
if (!Object.prototype.hasOwnProperty.call(this.forgeModMetadata, name)) {
const zip = new AdmZip(buf)
const zipEntries = zip.getEntries()
@@ -79,7 +79,7 @@ export class ForgeModStructure17 extends BaseForgeModStructure {
// Assuming the main mod will be the first entry in this file.
try {
const resolved = JSON.parse(raw) as object
if (resolved.hasOwnProperty('modListVersion')) {
if (Object.prototype.hasOwnProperty.call(resolved, 'modListVersion')) {
this.forgeModMetadata[name] = (resolved as McModInfoList).modList[0]
} else {
this.forgeModMetadata[name] = (resolved as McModInfo[])[0]

View File

@@ -1,10 +1,10 @@
import AdmZip from 'adm-zip'
import { Stats } from 'fs-extra'
import { Type } from 'helios-distribution-types'
import { join } from 'path'
import { resolve } from 'url'
import { capitalize } from '../../../../util/stringutils'
import { LiteMod } from '../../../liteloader/litemod'
import { Type } from '../../../spec/type'
import { ModuleStructure } from './module.struct'
export class LiteModStructure extends ModuleStructure {
@@ -26,15 +26,17 @@ export class LiteModStructure extends ModuleStructure {
protected async getModuleName(name: string, path: string, stats: Stats, buf: Buffer): Promise<string> {
return capitalize(this.getLiteModMetadata(buf, name).name)
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
protected async getModuleUrl(name: string, path: string, stats: Stats): Promise<string> {
return resolve(this.baseUrl, join(this.relativeRoot, name))
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
protected async getModulePath(name: string, path: string, stats: Stats): Promise<string | null> {
return null
}
private getLiteModMetadata(buf: Buffer, name: string): LiteMod {
if (!this.liteModMetadata.hasOwnProperty(name)) {
if (!Object.prototype.hasOwnProperty.call(this.liteModMetadata, name)) {
const zip = new AdmZip(buf)
const zipEntries = zip.getEntries()

View File

@@ -1,8 +1,7 @@
import { createHash } from 'crypto'
import { lstat, pathExists, readdir, readFile, Stats } from 'fs-extra'
import { Module, Type, TypeMetadata } from 'helios-distribution-types'
import { resolve } from 'path'
import { Module } from '../../../spec/module'
import { Type, TypeMetadata } from '../../../spec/type'
import { BaseModelStructure } from '../basemodel.struct'
export abstract class ModuleStructure extends BaseModelStructure<Module> {
@@ -25,7 +24,7 @@ export abstract class ModuleStructure extends BaseModelStructure<Module> {
return this.resolvedModels
}
protected generateMavenIdentifier(name: string, version: string) {
protected generateMavenIdentifier(name: string, version: string): string {
return `generated.${this.type.toLowerCase()}:${name}:${version}@${TypeMetadata[this.type].defaultExtension}`
}

View File

@@ -1,9 +1,9 @@
import { lstat, mkdirs, pathExists, readdir, readFile, writeFile } from 'fs-extra'
import { Server } from 'helios-distribution-types'
import { dirname, join, resolve as resolvePath } from 'path'
import { resolve as resolveUrl } from 'url'
import { VersionSegmentedRegistry } from '../../../util/VersionSegmentedRegistry'
import { ServerMeta } from '../../nebula/servermeta'
import { Server } from '../../spec/server'
import { BaseModelStructure } from './basemodel.struct'
import { MiscFileStructure } from './module/file.struct'
import { LiteModStructure } from './module/litemod.struct'
@@ -19,7 +19,7 @@ export class ServerStructure extends BaseModelStructure<Server> {
super(absoluteRoot, '', 'servers', baseUrl)
}
public async getSpecModel() {
public async getSpecModel(): Promise<Server[]> {
if (this.resolvedModels == null) {
this.resolvedModels = await this._doSeverRetrieval()
}
@@ -33,7 +33,7 @@ export class ServerStructure extends BaseModelStructure<Server> {
forgeVersion?: string
liteloaderVersion?: string
}
) {
): Promise<void> {
const effectiveId = `${id}-${minecraftVersion}`
const absoluteServerRoot = resolvePath(this.containerDirectory, effectiveId)
const relativeServerRoot = join(this.relativeRoot, effectiveId)
@@ -81,7 +81,7 @@ export class ServerStructure extends BaseModelStructure<Server> {
const match = this.ID_REGEX.exec(file)
if (match == null) {
console.warn(`Server directory ${file} does not match the defined standard.`)
console.warn(`All server ids must end with -<minecraft version> (ex. -1.12.2)`)
console.warn('All server ids must end with -<minecraft version> (ex. -1.12.2)')
continue
}

View File

@@ -15,38 +15,40 @@ export abstract class BaseMavenRepo extends BaseFileStructure {
super(absoluteRoot, relativeRoot, structRoot)
}
public getArtifactById(mavenIdentifier: string, extension?: string): string | null {
const resolved = MavenUtil.mavenIdentifierToString(mavenIdentifier, extension)
return resolved == null ? null : resolve(this.containerDirectory, resolved)
public getArtifactById(mavenIdentifier: string, extension?: string): string {
return resolve(this.containerDirectory, MavenUtil.mavenIdentifierToString(mavenIdentifier, extension))
}
public getArtifactByComponents(group: string, artifact: string, version: string,
classifier?: string, extension = 'jar'): string {
public getArtifactByComponents(
group: string, artifact: string, version: string, classifier?: string, extension = 'jar'
): string {
return resolve(this.containerDirectory,
MavenUtil.mavenComponentsToString(group, artifact, version, classifier, extension))
}
public getArtifactUrlByComponents(baseURL: string, group: string, artifact: string, version: string,
classifier?: string, extension = 'jar'): string {
public getArtifactUrlByComponents(
baseURL: string, group: string, artifact: string, version: string, classifier?: string, extension = 'jar'
): string {
return resolveURL(baseURL, join(this.relativeRoot,
MavenUtil.mavenComponentsToString(group, artifact, version, classifier, extension)))
}
public async artifactExists(path: string) {
public async artifactExists(path: string): Promise<boolean> {
return pathExists(path)
}
public async downloadArtifactById(url: string, mavenIdentifier: string, extension?: string) {
public async downloadArtifactById(url: string, mavenIdentifier: string, extension?: string): Promise<void> {
return this.downloadArtifactBase(url, MavenUtil.mavenIdentifierToString(mavenIdentifier, extension) as string)
}
public async downloadArtifactByComponents(url: string, group: string, artifact: string, version: string,
classifier?: string, extension?: string) {
public async downloadArtifactByComponents(
url: string, group: string, artifact: string, version: string, classifier?: string, extension?: string
): Promise<void> {
return this.downloadArtifactBase(url,
MavenUtil.mavenComponentsToString(group, artifact, version, classifier, extension))
}
private async downloadArtifactBase(url: string, relative: string) {
private async downloadArtifactBase(url: string, relative: string): Promise<void> {
const resolvedURL = resolveURL(url, relative).toString()
console.debug(`Downloading ${resolvedURL}..`)
const response = await axios({

View File

@@ -18,14 +18,14 @@ export class LibRepoStructure extends BaseMavenRepo {
super(absoluteRoot, relativeRoot, 'lib')
}
public getLocalForge(version: string, classifier?: string) {
public getLocalForge(version: string, classifier?: string): string {
return this.getArtifactByComponents(
LibRepoStructure.FORGE_GROUP,
LibRepoStructure.FORGE_ARTIFACT,
version, classifier, 'jar')
}
public getLocalLiteLoader(version: string, classifier?: string) {
public getLocalLiteLoader(version: string, classifier?: string): string {
return this.getArtifactByComponents(
LibRepoStructure.LITELOADER_GROUP,
LibRepoStructure.LITELOADER_ARTIFACT,

View File

@@ -17,25 +17,25 @@ export class RepoStructure extends BaseFileStructure {
this.versionRepoStruct = new VersionRepoStructure(this.containerDirectory, this.relativeRoot)
}
public async init() {
public async init(): Promise<void> {
super.init()
await this.libRepoStruct.init()
await this.versionRepoStruct.init()
}
public getLibRepoStruct() {
public getLibRepoStruct(): LibRepoStructure {
return this.libRepoStruct
}
public getVersionRepoStruct() {
public getVersionRepoStruct(): VersionRepoStructure {
return this.versionRepoStruct
}
public getTempDirectory() {
public getTempDirectory(): string {
return join(this.absoluteRoot, 'temp')
}
public getWorkDirectory() {
public getWorkDirectory(): string {
return join(this.absoluteRoot, 'work')
}

View File

@@ -11,16 +11,16 @@ export class VersionRepoStructure extends BaseFileStructure {
super(absoluteRoot, relativeRoot, 'versions')
}
public getFileName(minecraftVersion: string, forgeVersion: string) {
public getFileName(minecraftVersion: string, forgeVersion: string): string {
return `${minecraftVersion}-forge-${forgeVersion}`
}
public getVersionManifest(minecraftVersion: string, forgeVersion: string) {
public getVersionManifest(minecraftVersion: string, forgeVersion: string): string {
const fileName = this.getFileName(minecraftVersion, forgeVersion)
return join(this.containerDirectory, fileName, `${fileName}.json`)
}
public getVersionManifestURL(url: string, minecraftVersion: string, forgeVersion: string) {
public getVersionManifestURL(url: string, minecraftVersion: string, forgeVersion: string): string {
const fileName = this.getFileName(minecraftVersion, forgeVersion)
return resolveURL(url, join(this.relativeRoot, fileName, `${fileName}.json`))
}

View File

@@ -1,4 +1,4 @@
import { Module } from '../model/spec/module'
import { Module } from 'helios-distribution-types'
import { VersionSegmented } from '../util/VersionSegmented'
import { Resolver } from './resolver'

View File

@@ -1,9 +1,8 @@
import { spawn } from 'child_process'
import { copy, lstat, mkdirs, move, pathExists, readFile, remove, writeFile } from 'fs-extra'
import { Module, Type } from 'helios-distribution-types'
import { basename, dirname, join } from 'path'
import { VersionManifest113 } from '../../../model/forge/versionmanifest113'
import { Module } from '../../../model/spec/module'
import { Type } from '../../../model/spec/type'
import { LibRepoStructure } from '../../../model/struct/repo/librepo.struct'
import { JavaUtil } from '../../../util/javautil'
import { MavenUtil } from '../../../util/maven'
@@ -12,7 +11,7 @@ import { ForgeResolver } from '../forge.resolver'
export class Forge113Adapter extends ForgeResolver {
public static isForVersion(version: string) {
public static isForVersion(version: string): boolean {
return VersionUtil.isVersionAcceptable(version, [13, 14, 15])
}
@@ -34,12 +33,12 @@ export class Forge113Adapter extends ForgeResolver {
return Forge113Adapter.isForVersion(version)
}
private async process() {
private async process(): Promise<Module> {
const libRepo = this.repoStructure.getLibRepoStruct()
const installerPath = libRepo.getLocalForge(this.artifactVersion, 'installer')
console.debug(`Checking for forge installer at ${installerPath}..`)
if (!await libRepo.artifactExists(installerPath)) {
console.debug(`Forge installer not found locally, initializing download..`)
console.debug('Forge installer not found locally, initializing download..')
await libRepo.downloadArtifactByComponents(
this.REMOTE_REPOSITORY,
LibRepoStructure.FORGE_GROUP,
@@ -65,7 +64,7 @@ export class Forge113Adapter extends ForgeResolver {
// Required for the installer to function.
await writeFile(join(workDir, 'launcher_profiles.json'), JSON.stringify({}))
console.debug(`Spawning forge installer`)
console.debug('Spawning forge installer')
console.log('============== [ IMPORTANT ] ==============')
console.log('When the installer opens please set the client installation directory to:')
@@ -96,7 +95,7 @@ export class Forge113Adapter extends ForgeResolver {
return forgeModule
}
private async processLibraries(manifest: VersionManifest113) {
private async processLibraries(manifest: VersionManifest113): Promise<Module[]> {
const libDir = join(this.repoStructure.getWorkDirectory(), 'libraries')
const libRepo = this.repoStructure.getLibRepoStruct()
@@ -150,7 +149,7 @@ export class Forge113Adapter extends ForgeResolver {
}
private async processForgeModule(versionManifest: VersionManifest113) {
private async processForgeModule(versionManifest: VersionManifest113): Promise<Module> {
const libDir = join(this.repoStructure.getWorkDirectory(), 'libraries')
const mcpVersion = this.getMCPVersion(versionManifest.arguments.game)
@@ -267,7 +266,7 @@ export class Forge113Adapter extends ForgeResolver {
return forgeModule
}
private async processVersionManifest() {
private async processVersionManifest(): Promise<[VersionManifest113, Module]> {
const workDir = this.repoStructure.getWorkDirectory()
const versionRepo = this.repoStructure.getVersionRepoStruct()
const versionName = versionRepo.getFileName(this.minecraftVersion, this.forgeVersion)
@@ -297,8 +296,8 @@ export class Forge113Adapter extends ForgeResolver {
return [versionManifest, versionManifestModule]
}
private executeInstaller(installerExec: string) {
return new Promise((resolve, reject) => {
private executeInstaller(installerExec: string): Promise<void> {
return new Promise(resolve => {
const child = spawn(JavaUtil.getJavaExecutable(), [
'-jar',
installerExec
@@ -307,14 +306,14 @@ export class Forge113Adapter extends ForgeResolver {
})
child.stdout.on('data', (data) => console.log('[Forge Installer]', data.toString('utf8').trim()))
child.stderr.on('data', (data) => console.error('[Forge Installer]', data.toString('utf8').trim()))
child.on('close', (code, signal) => {
child.on('close', code => {
console.log('[Forge Installer]', 'Exited with code', code)
resolve()
})
})
}
private getMCPVersion(args: string[]) {
private getMCPVersion(args: string[]): string | null {
for (let i = 0; i < args.length; i++) {
if (args[i] === '--fml.mcpVersion') {
return args[i + 1]

View File

@@ -1,10 +1,9 @@
import AdmZip from 'adm-zip'
import { createHash } from 'crypto'
import { copy, lstat, mkdirs, pathExists, readFile, remove } from 'fs-extra'
import { Module, Type } from 'helios-distribution-types'
import { basename, join } from 'path'
import { VersionManifest17 } from '../../../model/forge/versionmanifest17'
import { Module } from '../../../model/spec/module'
import { Type } from '../../../model/spec/type'
import { LibRepoStructure } from '../../../model/struct/repo/librepo.struct'
import { MavenUtil } from '../../../util/maven'
import { PackXZExtractWrapper } from '../../../util/PackXZExtractWrapper'
@@ -13,7 +12,7 @@ import { ForgeResolver } from '../forge.resolver'
export class Forge17Adapter extends ForgeResolver {
public static isForVersion(version: string) {
public static isForVersion(version: string): boolean {
return VersionUtil.isVersionAcceptable(version, [7, 8, 9, 10, 11, 12])
}
@@ -31,16 +30,16 @@ export class Forge17Adapter extends ForgeResolver {
return this.getForgeByVersion()
}
public isForVersion(version: string) {
public isForVersion(version: string): boolean {
return Forge17Adapter.isForVersion(version)
}
public async getForgeByVersion() {
public async getForgeByVersion(): Promise<Module> {
const libRepo = this.repoStructure.getLibRepoStruct()
const targetLocalPath = libRepo.getLocalForge(this.artifactVersion, 'universal')
console.debug(`Checking for forge version at ${targetLocalPath}..`)
if (!await libRepo.artifactExists(targetLocalPath)) {
console.debug(`Forge not found locally, initializing download..`)
console.debug('Forge not found locally, initializing download..')
await libRepo.downloadArtifactByComponents(
this.REMOTE_REPOSITORY,
LibRepoStructure.FORGE_GROUP,
@@ -121,7 +120,7 @@ export class Forge17Adapter extends ForgeResolver {
}
}
} else {
console.debug(`Not found locally, downloading..`)
console.debug('Not found locally, downloading..')
queueDownload = true
}
@@ -176,12 +175,13 @@ export class Forge17Adapter extends ForgeResolver {
return forgeModule
}
private determineExtension(checksums: string[] | undefined) {
private determineExtension(checksums: string[] | undefined): string {
return checksums != null && checksums.length > 1 ? 'jar.pack.xz' : 'jar'
}
private async processPackXZFiles(
processingQueue: Array<{id: string, localPath: string}>): Promise<Array<{id: string, MD5: string}>> {
processingQueue: Array<{id: string, localPath: string}>
): Promise<Array<{id: string, MD5: string}>> {
const accumulator = []

View File

@@ -1,6 +1,6 @@
import { createHash } from 'crypto'
import { Stats } from 'fs-extra'
import { Artifact } from '../../model/spec/artifact'
import { Artifact } from 'helios-distribution-types'
import { RepoStructure } from '../../model/struct/repo/repo.struct'
import { BaseResolver } from '../baseresolver'
@@ -25,7 +25,7 @@ export abstract class ForgeResolver extends BaseResolver {
// Coverage is not 100% but that doesnt matter.
// It's enough and you should always use the latest version anyway.
public inferArtifactVersion() {
public inferArtifactVersion(): string {
const version = `${this.minecraftVersion}-${this.forgeVersion}`
const ver = this.forgeVersion.split('.')

View File

@@ -1,4 +1,4 @@
import { Module } from '../model/spec/module'
import { Module } from 'helios-distribution-types'
export interface Resolver {

View File

@@ -4,24 +4,24 @@ import { JavaUtil } from './javautil'
export class PackXZExtractWrapper {
public static getPackXZExtract() {
public static getPackXZExtract(): string {
return join(process.cwd(), 'libraries', 'java', 'PackXZExtract.jar')
}
public static extractUnpack(paths: string[]) {
public static extractUnpack(paths: string[]): Promise<void> {
return PackXZExtractWrapper.execute('-packxz', paths)
}
public static extract(paths: string[]) {
public static extract(paths: string[]): Promise<void> {
return PackXZExtractWrapper.execute('-xz', paths)
}
public static unpack(paths: string[]) {
public static unpack(paths: string[]): Promise<void> {
return PackXZExtractWrapper.execute('-pack', paths)
}
private static execute(command: string, paths: string[]) {
return new Promise((resolve, reject) => {
private static execute(command: string, paths: string[]): Promise<void> {
return new Promise(resolve => {
const child = spawn(JavaUtil.getJavaExecutable(), [
'-jar',
PackXZExtractWrapper.getPackXZExtract(),
@@ -30,7 +30,7 @@ export class PackXZExtractWrapper {
])
child.stdout.on('data', (data) => console.log('[PackXZExtract]', data.toString('utf8').trim()))
child.stderr.on('data', (data) => console.error('[PackXZExtract]', data.toString('utf8').trim()))
child.on('close', (code, signal) => {
child.on('close', code => {
console.log('[PackXZExtract]', 'Exited with code', code)
resolve()
})

View File

@@ -2,6 +2,8 @@ import { ForgeModStructure113 } from '../model/struct/model/module/forgemod/forg
import { ForgeModStructure17 } from '../model/struct/model/module/forgemod/forgemod17.struct'
import { Forge113Adapter } from '../resolver/forge/adapter/forge113.resolver'
import { Forge17Adapter } from '../resolver/forge/adapter/forge17.resolver'
import { ForgeResolver } from '../resolver/forge/forge.resolver'
import { BaseForgeModStructure } from '../model/struct/model/module/forgemod.struct'
export class VersionSegmentedRegistry {
@@ -20,7 +22,8 @@ export class VersionSegmentedRegistry {
forgeVersion: string,
absoluteRoot: string,
relativeRoot: string,
baseURL: string) {
baseURL: string
): ForgeResolver {
for (const impl of VersionSegmentedRegistry.FORGE_ADAPTER_IMPL) {
if (impl.isForVersion(minecraftVersion)) {
return new impl(absoluteRoot, relativeRoot, baseURL, minecraftVersion, forgeVersion)
@@ -34,7 +37,7 @@ export class VersionSegmentedRegistry {
absoluteRoot: string,
relativeRoot: string,
baseUrl: string
) {
): BaseForgeModStructure {
for (const impl of VersionSegmentedRegistry.FORGEMOD_STRUCT_IML) {
if (impl.isForVersion(minecraftVersion)) {
return new impl(absoluteRoot, relativeRoot, baseUrl)

View File

@@ -1,6 +1,6 @@
export class JavaUtil {
public static getJavaExecutable() {
public static getJavaExecutable(): string {
return process.env.JAVA_EXECUTABLE as string
}

View File

@@ -6,8 +6,13 @@ export class MavenUtil {
public static readonly ID_REGEX = /(.+):(.+):([^@]+)()(?:@{1}(.+)$)?/
public static readonly ID_REGEX_WITH_CLASSIFIER = /(.+):(.+):(?:([^@]+)(?:-([a-zA-Z]+)))(?:@{1}(.+)$)?/
public static mavenComponentsToIdentifier(group: string, artifact: string, version: string,
classifier?: string, extension?: string) {
public static mavenComponentsToIdentifier(
group: string,
artifact: string,
version: string,
classifier?: string,
extension?: string
): string {
return `${group}:${artifact}:${version}${classifier != null ? `:${classifier}` : ''}${extension != null ? `@${extension}` : ''}`
}
@@ -15,7 +20,13 @@ export class MavenUtil {
return MavenUtil.ID_REGEX.test(id) || MavenUtil.ID_REGEX_WITH_CLASSIFIER.test(id)
}
public static getMavenComponents(id: string, extension = 'jar') {
public static getMavenComponents(id: string, extension = 'jar'): {
group: string
artifact: string
version: string
classifier?: string
extension: string
} {
if (!MavenUtil.isMavenIdentifier(id)) {
throw new Error('Id is not a maven identifier.')
}
@@ -41,39 +52,37 @@ export class MavenUtil {
throw new Error('Failed to process maven data.')
}
public static mavenIdentifierToString(id: string, extension = 'jar') {
public static mavenIdentifierToString(id: string, extension = 'jar'): string {
const tmp = MavenUtil.getMavenComponents(id, extension)
if (tmp != null) {
return MavenUtil.mavenComponentsToString(tmp.group, tmp.artifact, tmp.version,
tmp.classifier, tmp.extension)
} else {
return null
}
return MavenUtil.mavenComponentsToString(
tmp.group, tmp.artifact, tmp.version, tmp.classifier, tmp.extension
)
}
public static mavenComponentsToString(group: string, artifact: string, version: string,
classifier?: string, extension = 'jar') {
public static mavenComponentsToString(
group: string, artifact: string, version: string, classifier?: string, extension = 'jar'
): string {
return `${group.replace(/\./g, '/')}/${artifact}/${version}/${artifact}-${version}${classifier != null ? `-${classifier}` : ''}.${extension}`
}
public static mavenIdentifierToUrl(id: string, extension = 'jar') {
const res = MavenUtil.mavenIdentifierToString(id, extension)
return res == null ? null : new URL(res)
public static mavenIdentifierToUrl(id: string, extension = 'jar'): URL {
return new URL(MavenUtil.mavenIdentifierToString(id, extension))
}
public static mavenComponentsToUrl(group: string, artifact: string, version: string,
classifier?: string, extension = 'jar') {
public static mavenComponentsToUrl(
group: string, artifact: string, version: string, classifier?: string, extension = 'jar'
): URL {
return new URL(MavenUtil.mavenComponentsToString(group, artifact, version, classifier, extension))
}
public static mavenIdentifierToPath(id: string, extension = 'jar') {
const res = MavenUtil.mavenIdentifierToString(id, extension)
return res == null ? null : normalize(res)
public static mavenIdentifierToPath(id: string, extension = 'jar'): string {
return normalize(MavenUtil.mavenIdentifierToString(id, extension))
}
public static mavenComponentsToPath(group: string, artifact: string, version: string,
classifier?: string, extension = 'jar') {
public static mavenComponentsToPath(
group: string, artifact: string, version: string, classifier?: string, extension = 'jar'
): string {
return normalize(MavenUtil.mavenComponentsToString(group, artifact, version, classifier, extension))
}

View File

@@ -1,4 +1,4 @@
export function capitalize(str: string) {
export function capitalize(str: string): string {
if (!str) {
return str
}

View File

@@ -10,11 +10,11 @@ export class VersionUtil {
public static readonly MINECRAFT_VERSION_REGEX = /(\d+).(\d+).(\d+)/
public static isMinecraftVersion(version: string) {
public static isMinecraftVersion(version: string): boolean {
return VersionUtil.MINECRAFT_VERSION_REGEX.test(version)
}
public static getMinecraftVersionComponents(version: string) {
public static getMinecraftVersionComponents(version: string): { major: number, minor: number, revision: number } {
if (VersionUtil.isMinecraftVersion(version)) {
const result = VersionUtil.MINECRAFT_VERSION_REGEX.exec(version)
if (result != null) {
@@ -36,11 +36,11 @@ export class VersionUtil {
return false
}
public static isPromotionVersion(version: string) {
public static isPromotionVersion(version: string): boolean {
return VersionUtil.PROMOTION_TYPE.indexOf(version.toLowerCase()) > -1
}
public static async getPromotionIndex() {
public static async getPromotionIndex(): Promise<PromotionsSlim> {
const response = await Axios({
method: 'get',
url: 'https://files.minecraftforge.net/maven/net/minecraftforge/forge/promotions_slim.json',
@@ -49,18 +49,18 @@ export class VersionUtil {
return response.data as PromotionsSlim
}
public static getPromotedVersionStrict(index: PromotionsSlim, minecraftVersion: string, promotion: string) {
public static getPromotedVersionStrict(index: PromotionsSlim, minecraftVersion: string, promotion: string): string {
const workingPromotion = promotion.toLowerCase()
return index.promos[`${minecraftVersion}-${workingPromotion}`]
}
public static async getPromotedForgeVersion(minecraftVersion: string, promotion: string) {
public static async getPromotedForgeVersion(minecraftVersion: string, promotion: string): Promise<string> {
const workingPromotion = promotion.toLowerCase()
const res = await VersionUtil.getPromotionIndex()
let version = res.promos[`${minecraftVersion}-${workingPromotion}`]
if (version == null) {
console.warn(`No ${workingPromotion} version found for Forge ${minecraftVersion}.`)
console.warn(`Attempting to pull latest version instead.`)
console.warn('Attempting to pull latest version instead.')
version = res.promos[`${minecraftVersion}-latest`]
if (version == null) {
throw new Error(`No latest version found for Forge ${minecraftVersion}.`)