cyclops-infobook-html 5.1.0 → 5.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -12
- package/bin/compress-icons.js +42 -7
- package/bin/convert-pom-to-modpack.d.ts +2 -0
- package/bin/convert-pom-to-modpack.js +96 -0
- package/bin/generate-icons.js +50 -15
- package/bin/generate-infobook-html.js +46 -10
- package/bin/generate-mod-metadata.js +52 -14
- package/index.d.ts +25 -25
- package/lib/icon/IconsCompressor.js +43 -12
- package/lib/icon/IconsGenerator.d.ts +42 -20
- package/lib/icon/IconsGenerator.js +156 -70
- package/lib/infobook/FileWriter.d.ts +3 -3
- package/lib/infobook/FileWriter.js +5 -5
- package/lib/infobook/IFileWriter.d.ts +2 -2
- package/lib/infobook/IInfoAppendix.d.ts +3 -3
- package/lib/infobook/IInfoBook.d.ts +2 -4
- package/lib/infobook/IInfoSection.d.ts +1 -1
- package/lib/infobook/IInfobookPlugin.d.ts +5 -5
- package/lib/infobook/InfoBookInitializer.d.ts +7 -9
- package/lib/infobook/InfoBookInitializer.js +1 -1
- package/lib/infobook/appendix/IInfoBookAppendixHandler.d.ts +2 -2
- package/lib/infobook/appendix/InfoBookAppendixAd.d.ts +3 -3
- package/lib/infobook/appendix/InfoBookAppendixAd.js +7 -2
- package/lib/infobook/appendix/InfoBookAppendixHandlerAbstractRecipe.d.ts +12 -14
- package/lib/infobook/appendix/InfoBookAppendixHandlerAbstractRecipe.js +41 -8
- package/lib/infobook/appendix/InfoBookAppendixHandlerAdvancementRewards.d.ts +3 -3
- package/lib/infobook/appendix/InfoBookAppendixHandlerAdvancementRewards.js +5 -4
- package/lib/infobook/appendix/InfoBookAppendixHandlerCraftingRecipe.d.ts +6 -5
- package/lib/infobook/appendix/InfoBookAppendixHandlerCraftingRecipe.js +4 -3
- package/lib/infobook/appendix/InfoBookAppendixHandlerImage.d.ts +3 -3
- package/lib/infobook/appendix/InfoBookAppendixHandlerImage.js +4 -4
- package/lib/infobook/appendix/InfoBookAppendixHandlerKeybinding.d.ts +3 -3
- package/lib/infobook/appendix/InfoBookAppendixHandlerKeybinding.js +3 -2
- package/lib/infobook/appendix/InfoBookAppendixHandlerSmeltingRecipe.d.ts +6 -5
- package/lib/infobook/appendix/InfoBookAppendixHandlerSmeltingRecipe.js +3 -2
- package/lib/infobook/appendix/InfoBookAppendixHandlerTextfield.d.ts +3 -3
- package/lib/infobook/appendix/InfoBookAppendixHandlerTextfield.js +4 -5
- package/lib/infobook/appendix/InfoBookAppendixTagIndex.d.ts +4 -4
- package/lib/infobook/appendix/InfoBookAppendixTagIndex.js +2 -1
- package/lib/modloader/ModLoader.d.ts +13 -2
- package/lib/modloader/ModLoader.js +164 -69
- package/lib/modloader/PomConverter.d.ts +28 -0
- package/lib/modloader/PomConverter.js +194 -0
- package/lib/parse/XmlInfoBookParser.d.ts +5 -7
- package/lib/parse/XmlInfoBookParser.js +42 -9
- package/lib/resource/ResourceHandler.d.ts +7 -13
- package/lib/resource/ResourceHandler.js +16 -11
- package/lib/resource/ResourceLoader.d.ts +4 -5
- package/lib/resource/ResourceLoader.js +35 -33
- package/lib/serialize/HtmlInfoBookSerializer.d.ts +10 -16
- package/lib/serialize/HtmlInfoBookSerializer.js +83 -80
- package/package.json +33 -27
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
36
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
37
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -8,19 +41,22 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
41
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
42
|
});
|
|
10
43
|
};
|
|
44
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
45
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
46
|
+
};
|
|
11
47
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
48
|
exports.ModLoader = void 0;
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const fs = require("fs");
|
|
16
|
-
const
|
|
49
|
+
const node_child_process_1 = require("node:child_process");
|
|
50
|
+
const node_fs_1 = require("node:fs");
|
|
51
|
+
const fs = __importStar(require("node:fs"));
|
|
52
|
+
const Path = __importStar(require("node:path"));
|
|
53
|
+
const node_path_1 = require("node:path");
|
|
54
|
+
const node_util_1 = require("node:util");
|
|
55
|
+
const mvn_artifact_download_1 = __importDefault(require("mvn-artifact-download"));
|
|
17
56
|
const ncp_1 = require("ncp");
|
|
18
|
-
const node_fetch_1 = require("node-fetch");
|
|
19
|
-
const
|
|
20
|
-
const rimraf = require("rimraf");
|
|
21
|
-
const util_1 = require("util");
|
|
57
|
+
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
58
|
+
const rimraf_1 = __importDefault(require("rimraf"));
|
|
22
59
|
const yauzl_1 = require("yauzl");
|
|
23
|
-
const Path = require("path");
|
|
24
60
|
/**
|
|
25
61
|
* Takes care of installing Forge, installing mods, starting a Forge server, and fetching metadata.
|
|
26
62
|
*/
|
|
@@ -54,13 +90,19 @@ class ModLoader {
|
|
|
54
90
|
if (!res.ok) {
|
|
55
91
|
throw new Error(`Failed to fetch (${res.statusText}): ${forgeInstaller}`);
|
|
56
92
|
}
|
|
57
|
-
installerFile = (0,
|
|
58
|
-
|
|
59
|
-
|
|
93
|
+
installerFile = (0, node_path_1.join)(this.path, 'forge-installer.jar');
|
|
94
|
+
const forgeBuffer = yield res.buffer();
|
|
95
|
+
yield new Promise((resolve, reject) => fs.writeFile(installerFile, forgeBuffer, (err) => {
|
|
96
|
+
if (err) {
|
|
97
|
+
reject(err);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
resolve();
|
|
101
|
+
}
|
|
60
102
|
}));
|
|
61
103
|
// Install Forge
|
|
62
104
|
process.stdout.write('Installing Forge...\n');
|
|
63
|
-
yield new Promise(
|
|
105
|
+
yield new Promise(resolve => (0, node_child_process_1.exec)(`cd ${this.path} && java -jar forge-installer.jar --installServer`).on('exit', resolve));
|
|
64
106
|
}
|
|
65
107
|
else {
|
|
66
108
|
// Download NeoForge installer
|
|
@@ -70,21 +112,27 @@ class ModLoader {
|
|
|
70
112
|
if (!res.ok) {
|
|
71
113
|
throw new Error(`Failed to fetch (${res.statusText}): ${installer}`);
|
|
72
114
|
}
|
|
73
|
-
installerFile = (0,
|
|
74
|
-
|
|
75
|
-
|
|
115
|
+
installerFile = (0, node_path_1.join)(this.path, 'neoforge-installer.jar');
|
|
116
|
+
const neoBuffer = yield res.buffer();
|
|
117
|
+
yield new Promise((resolve, reject) => fs.writeFile(installerFile, neoBuffer, (err) => {
|
|
118
|
+
if (err) {
|
|
119
|
+
reject(err);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
resolve();
|
|
123
|
+
}
|
|
76
124
|
}));
|
|
77
125
|
// Install Forge
|
|
78
126
|
process.stdout.write('Installing NeoForge...\n');
|
|
79
|
-
yield new Promise(
|
|
127
|
+
yield new Promise(resolve => (0, node_child_process_1.exec)(`cd ${this.path} && java -jar neoforge-installer.jar --installServer`).on('exit', resolve));
|
|
80
128
|
}
|
|
81
129
|
// Wait a bit, because otherwise some files don't exist yet (while they should...)
|
|
82
130
|
process.stdout.write('Wait a bit after mod loader installation...\n');
|
|
83
|
-
yield new Promise(
|
|
131
|
+
yield new Promise(resolve => setTimeout(resolve, 10000));
|
|
84
132
|
// Cleanup
|
|
85
133
|
process.stdout.write('Cleaning up...\n');
|
|
86
134
|
yield fs.promises.unlink(installerFile);
|
|
87
|
-
yield fs.promises.unlink(installerFile
|
|
135
|
+
yield fs.promises.unlink(`${installerFile}.log`);
|
|
88
136
|
});
|
|
89
137
|
}
|
|
90
138
|
/**
|
|
@@ -93,14 +141,14 @@ class ModLoader {
|
|
|
93
141
|
acceptEula() {
|
|
94
142
|
return __awaiter(this, void 0, void 0, function* () {
|
|
95
143
|
process.stdout.write('Accepting EULA...\n');
|
|
96
|
-
yield fs.promises.writeFile((0,
|
|
144
|
+
yield fs.promises.writeFile((0, node_path_1.join)(this.path, 'eula.txt'), 'eula=true');
|
|
97
145
|
});
|
|
98
146
|
}
|
|
99
147
|
/**
|
|
100
148
|
* @returns {boolean} If mods are installed.
|
|
101
149
|
*/
|
|
102
150
|
areModsInstalled() {
|
|
103
|
-
return fs.existsSync((0,
|
|
151
|
+
return fs.existsSync((0, node_path_1.join)(this.path, 'mods'));
|
|
104
152
|
}
|
|
105
153
|
/**
|
|
106
154
|
* Download and install mods.
|
|
@@ -108,7 +156,7 @@ class ModLoader {
|
|
|
108
156
|
installMods() {
|
|
109
157
|
return __awaiter(this, void 0, void 0, function* () {
|
|
110
158
|
process.stdout.write('Downloading mods...\n');
|
|
111
|
-
const modsDir = (0,
|
|
159
|
+
const modsDir = (0, node_path_1.join)(this.path, 'mods');
|
|
112
160
|
if (!fs.existsSync(modsDir)) {
|
|
113
161
|
yield fs.promises.mkdir(modsDir);
|
|
114
162
|
}
|
|
@@ -117,15 +165,22 @@ class ModLoader {
|
|
|
117
165
|
const fileName = `${mod.artifact}-${mod.version}.jar`;
|
|
118
166
|
process.stdout.write(` - ${fileName} from CurseForge...\n`);
|
|
119
167
|
const url = `https://minecraft.curseforge.com/api/maven/${mod.project}/${mod.artifact
|
|
120
|
-
.
|
|
168
|
+
.replaceAll('-', '/')}/${fileName}`;
|
|
121
169
|
yield this.downloadFile(url, fileName, modsDir);
|
|
122
170
|
}
|
|
123
171
|
else if (mod.type === 'maven') {
|
|
124
172
|
process.stdout.write(` - ${mod.artifact} from ${mod.repo}...\n`);
|
|
125
|
-
|
|
173
|
+
let name;
|
|
174
|
+
if (mod.headers && Object.keys(mod.headers).length > 0) {
|
|
175
|
+
// Authenticated download: construct URL manually and use fetch with headers
|
|
176
|
+
name = yield this.downloadMavenArtifact(mod.artifact, mod.repo, modsDir, mod.headers);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
name = yield (0, mvn_artifact_download_1.default)(mod.artifact, modsDir, mod.repo);
|
|
180
|
+
}
|
|
126
181
|
// Rename file if needed
|
|
127
182
|
if ('name' in mod) {
|
|
128
|
-
fs.renameSync(name, (0,
|
|
183
|
+
fs.renameSync(name, (0, node_path_1.join)(modsDir, mod.name));
|
|
129
184
|
}
|
|
130
185
|
}
|
|
131
186
|
else if (mod.type === 'raw') {
|
|
@@ -133,25 +188,64 @@ class ModLoader {
|
|
|
133
188
|
yield this.downloadFile(mod.url, mod.name, modsDir);
|
|
134
189
|
}
|
|
135
190
|
else {
|
|
136
|
-
throw new Error(
|
|
191
|
+
throw new Error(`Unknown mod type ${mod.type}`);
|
|
137
192
|
}
|
|
138
193
|
}
|
|
139
194
|
});
|
|
140
195
|
}
|
|
141
|
-
downloadFile(url, fileName, modsDir) {
|
|
196
|
+
downloadFile(url, fileName, modsDir, headers) {
|
|
142
197
|
return __awaiter(this, void 0, void 0, function* () {
|
|
143
|
-
const response = yield (0, node_fetch_1.default)(url);
|
|
198
|
+
const response = yield (0, node_fetch_1.default)(url, headers ? { headers } : undefined);
|
|
144
199
|
if (response.status !== 200) {
|
|
145
|
-
throw new Error(response.statusText
|
|
200
|
+
throw new Error(`${response.statusText} on ${url}`);
|
|
146
201
|
}
|
|
147
202
|
yield new Promise((resolve, reject) => {
|
|
148
203
|
response.body
|
|
149
204
|
.on('error', reject)
|
|
150
205
|
.on('end', resolve)
|
|
151
|
-
.pipe(fs.createWriteStream((0,
|
|
206
|
+
.pipe(fs.createWriteStream((0, node_path_1.join)(modsDir, fileName)));
|
|
152
207
|
});
|
|
153
208
|
});
|
|
154
209
|
}
|
|
210
|
+
/**
|
|
211
|
+
* Download a Maven artifact by constructing the repository URL manually.
|
|
212
|
+
* This is used for authenticated repositories where custom headers are needed.
|
|
213
|
+
* @param artifact The Maven artifact coordinates (groupId:artifactId:version[:classifier]).
|
|
214
|
+
* @param repoUrl The base URL of the Maven repository.
|
|
215
|
+
* @param modsDir The directory to save the downloaded file.
|
|
216
|
+
* @param headers Optional HTTP headers (e.g., Authorization).
|
|
217
|
+
* @returns The full path to the downloaded file.
|
|
218
|
+
*/
|
|
219
|
+
downloadMavenArtifact(artifact, repoUrl, modsDir, headers) {
|
|
220
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
221
|
+
const parts = artifact.split(':');
|
|
222
|
+
if (parts.length < 3) {
|
|
223
|
+
throw new Error(`Invalid Maven artifact: ${artifact}. Expected format: groupId:artifactId:version[:classifier]`);
|
|
224
|
+
}
|
|
225
|
+
const [groupId, artifactId, version, classifier] = parts;
|
|
226
|
+
const groupPath = groupId.replaceAll('.', '/');
|
|
227
|
+
const suffix = classifier ? `-${classifier}` : '';
|
|
228
|
+
const fileName = `${artifactId}-${version}${suffix}.jar`;
|
|
229
|
+
const base = repoUrl.endsWith('/') ? repoUrl : `${repoUrl}/`;
|
|
230
|
+
const url = `${base}${groupPath}/${artifactId}/${version}/${fileName}`;
|
|
231
|
+
// Expand environment variable placeholders in header values (e.g. ${GITHUB_TOKEN})
|
|
232
|
+
const resolvedHeaders = {};
|
|
233
|
+
if (headers) {
|
|
234
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
235
|
+
resolvedHeaders[key] = value.replaceAll(/\$\{([^}]+)\}/gu, (fullMatch, name) => {
|
|
236
|
+
const envValue = process.env[name];
|
|
237
|
+
if (envValue === undefined) {
|
|
238
|
+
process.stderr.write(`Warning: environment variable '${name}' is not set; the request header '${key}' may be invalid\n`);
|
|
239
|
+
return fullMatch;
|
|
240
|
+
}
|
|
241
|
+
return envValue;
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
yield this.downloadFile(url, fileName, modsDir, resolvedHeaders);
|
|
246
|
+
return (0, node_path_1.join)(modsDir, fileName);
|
|
247
|
+
});
|
|
248
|
+
}
|
|
155
249
|
/**
|
|
156
250
|
* Start the server and execute a command to dump all registries
|
|
157
251
|
*/
|
|
@@ -159,7 +253,7 @@ class ModLoader {
|
|
|
159
253
|
return __awaiter(this, void 0, void 0, function* () {
|
|
160
254
|
// Start the Forge server
|
|
161
255
|
process.stdout.write('Starting server...\n');
|
|
162
|
-
const proc = (0,
|
|
256
|
+
const proc = (0, node_child_process_1.exec)(`cd ${this.path} && ./run.sh nogui`);
|
|
163
257
|
// Ignore stdout: proc.stdout.pipe(process.stdout);
|
|
164
258
|
proc.stderr.pipe(process.stderr);
|
|
165
259
|
const onDone = new Promise((resolve, reject) => {
|
|
@@ -168,14 +262,14 @@ class ModLoader {
|
|
|
168
262
|
resolve();
|
|
169
263
|
}
|
|
170
264
|
else {
|
|
171
|
-
reject('Server closed with non-zero exit code');
|
|
265
|
+
reject(new Error('Server closed with non-zero exit code'));
|
|
172
266
|
}
|
|
173
267
|
});
|
|
174
268
|
proc.addListener('error', reject);
|
|
175
269
|
});
|
|
176
270
|
// Once the loading is complete, send our command and stop the server
|
|
177
271
|
proc.stdout.on('data', (line) => {
|
|
178
|
-
if (line.
|
|
272
|
+
if (line.includes('Done') && line.includes('For help, type "help"')) {
|
|
179
273
|
process.stdout.write('Dumping registries...\n');
|
|
180
274
|
this.sendCommand(proc, '/cyclopscore dumpregistries');
|
|
181
275
|
this.sendCommand(proc, '/stop');
|
|
@@ -190,7 +284,7 @@ class ModLoader {
|
|
|
190
284
|
* @param {string} command A command.
|
|
191
285
|
*/
|
|
192
286
|
sendCommand(proc, command) {
|
|
193
|
-
proc.stdin.write(command
|
|
287
|
+
proc.stdin.write(`${command}\n`);
|
|
194
288
|
}
|
|
195
289
|
/**
|
|
196
290
|
* Copy the resulting registry files to a target path.
|
|
@@ -199,10 +293,10 @@ class ModLoader {
|
|
|
199
293
|
copyRegistries(target) {
|
|
200
294
|
return __awaiter(this, void 0, void 0, function* () {
|
|
201
295
|
process.stdout.write('Copying registries...\n');
|
|
202
|
-
if (!fs.existsSync((0,
|
|
203
|
-
yield fs.promises.mkdir((0,
|
|
296
|
+
if (!fs.existsSync((0, node_path_1.join)(this.path, 'cyclops_registries'))) {
|
|
297
|
+
yield fs.promises.mkdir((0, node_path_1.join)(this.path, 'cyclops_registries'));
|
|
204
298
|
}
|
|
205
|
-
yield (0,
|
|
299
|
+
yield (0, node_util_1.promisify)(ncp_1.ncp)((0, node_path_1.join)(this.path, 'cyclops_registries'), target);
|
|
206
300
|
});
|
|
207
301
|
}
|
|
208
302
|
/**
|
|
@@ -211,8 +305,8 @@ class ModLoader {
|
|
|
211
305
|
extractMinecraftAssets() {
|
|
212
306
|
return __awaiter(this, void 0, void 0, function* () {
|
|
213
307
|
process.stdout.write('Extracting minecraft assets...\n');
|
|
214
|
-
if (!fs.existsSync((0,
|
|
215
|
-
yield fs.promises.mkdir((0,
|
|
308
|
+
if (!fs.existsSync((0, node_path_1.join)(this.path, 'mc_assets'))) {
|
|
309
|
+
yield fs.promises.mkdir((0, node_path_1.join)(this.path, 'mc_assets'));
|
|
216
310
|
}
|
|
217
311
|
// Find Minecraft jar
|
|
218
312
|
let jar = null;
|
|
@@ -221,14 +315,14 @@ class ModLoader {
|
|
|
221
315
|
if (dir.indexOf('-') > 0) {
|
|
222
316
|
for (const file of yield fs.promises.readdir(Path.join(subPath, dir))) {
|
|
223
317
|
if (file.startsWith('server') && file.endsWith('extra.jar')) {
|
|
224
|
-
jar = (0,
|
|
318
|
+
jar = (0, node_path_1.join)(subPath, dir, file);
|
|
225
319
|
}
|
|
226
320
|
}
|
|
227
321
|
}
|
|
228
322
|
}
|
|
229
323
|
// Error if no jar was found
|
|
230
324
|
if (!jar) {
|
|
231
|
-
throw new Error(
|
|
325
|
+
throw new Error(`Could not find a valid minecraft server in ${this.path}`);
|
|
232
326
|
}
|
|
233
327
|
// Unzip the jar
|
|
234
328
|
process.stdout.write(`Extracting Minecraft jar...\n`);
|
|
@@ -241,14 +335,14 @@ class ModLoader {
|
|
|
241
335
|
extractModsAssets() {
|
|
242
336
|
return __awaiter(this, void 0, void 0, function* () {
|
|
243
337
|
process.stdout.write('Extracting mod assets...\n');
|
|
244
|
-
if (!fs.existsSync((0,
|
|
245
|
-
yield fs.promises.mkdir((0,
|
|
338
|
+
if (!fs.existsSync((0, node_path_1.join)(this.path, 'mod_assets'))) {
|
|
339
|
+
yield fs.promises.mkdir((0, node_path_1.join)(this.path, 'mod_assets'));
|
|
246
340
|
}
|
|
247
341
|
// Loop over all mods
|
|
248
|
-
const modsDir = (0,
|
|
342
|
+
const modsDir = (0, node_path_1.join)(this.path, 'mods');
|
|
249
343
|
for (const mod of yield fs.promises.readdir(modsDir)) {
|
|
250
344
|
if (mod.endsWith('.jar')) {
|
|
251
|
-
const modFile = (0,
|
|
345
|
+
const modFile = (0, node_path_1.join)(modsDir, mod);
|
|
252
346
|
process.stdout.write(` - ${mod}...\n`);
|
|
253
347
|
yield this.extractModAssets(modFile);
|
|
254
348
|
}
|
|
@@ -270,33 +364,34 @@ class ModLoader {
|
|
|
270
364
|
});
|
|
271
365
|
});
|
|
272
366
|
zipFile.readEntry();
|
|
273
|
-
zipFile.on('error',
|
|
367
|
+
zipFile.on('error', e => process.stdout.write(String(e)));
|
|
274
368
|
zipFile.on('entry', (entry) => {
|
|
275
369
|
if (entry.fileName.endsWith('/')) {
|
|
276
370
|
// Directory
|
|
277
371
|
zipFile.readEntry();
|
|
278
372
|
}
|
|
279
|
-
else {
|
|
373
|
+
else if (entry.fileName.startsWith('assets/') || entry.fileName.startsWith('data/')) {
|
|
280
374
|
// File
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
});
|
|
375
|
+
const prefix = entry.fileName.startsWith('assets/') ? 7 : 5;
|
|
376
|
+
const targetFile = (0, node_path_1.join)(this.path, 'mod_assets', entry.fileName.slice(prefix, entry.fileName.length));
|
|
377
|
+
const targetDir = (0, node_path_1.dirname)(targetFile);
|
|
378
|
+
void this.ensureDirExists(targetDir).then(() => {
|
|
379
|
+
zipFile.openReadStream(entry, (e, readStream) => {
|
|
380
|
+
if (e) {
|
|
381
|
+
throw e;
|
|
382
|
+
}
|
|
383
|
+
readStream.pipe((0, node_fs_1.createWriteStream)(targetFile));
|
|
384
|
+
readStream.on('end', () => zipFile.readEntry());
|
|
292
385
|
});
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
386
|
+
}).catch((e) => {
|
|
387
|
+
throw e;
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
zipFile.readEntry();
|
|
297
392
|
}
|
|
298
393
|
});
|
|
299
|
-
yield new Promise(
|
|
394
|
+
yield new Promise(resolve => zipFile.on('end', resolve));
|
|
300
395
|
});
|
|
301
396
|
}
|
|
302
397
|
/**
|
|
@@ -306,7 +401,7 @@ class ModLoader {
|
|
|
306
401
|
copyModAssets(target) {
|
|
307
402
|
return __awaiter(this, void 0, void 0, function* () {
|
|
308
403
|
process.stdout.write('Copying mod assets...\n');
|
|
309
|
-
yield (0,
|
|
404
|
+
yield (0, node_util_1.promisify)(ncp_1.ncp)((0, node_path_1.join)(this.path, 'mod_assets'), target);
|
|
310
405
|
});
|
|
311
406
|
}
|
|
312
407
|
/**
|
|
@@ -314,7 +409,7 @@ class ModLoader {
|
|
|
314
409
|
*/
|
|
315
410
|
removeServer() {
|
|
316
411
|
return __awaiter(this, void 0, void 0, function* () {
|
|
317
|
-
yield (0,
|
|
412
|
+
yield (0, node_util_1.promisify)(rimraf_1.default)(this.path);
|
|
318
413
|
});
|
|
319
414
|
}
|
|
320
415
|
/**
|
|
@@ -322,18 +417,18 @@ class ModLoader {
|
|
|
322
417
|
*/
|
|
323
418
|
removeMods() {
|
|
324
419
|
return __awaiter(this, void 0, void 0, function* () {
|
|
325
|
-
yield (0,
|
|
420
|
+
yield (0, node_util_1.promisify)(rimraf_1.default)((0, node_path_1.join)(this.path, 'mods'));
|
|
326
421
|
});
|
|
327
422
|
}
|
|
328
423
|
ensureDirExists(path) {
|
|
329
424
|
return __awaiter(this, void 0, void 0, function* () {
|
|
330
|
-
const segments = path.
|
|
425
|
+
const segments = path.slice(this.path.length, this.path.length + path.length).split(node_path_1.sep);
|
|
331
426
|
for (let i = 1; i <= segments.length; i++) {
|
|
332
|
-
const subPath = (0,
|
|
427
|
+
const subPath = (0, node_path_1.join)(this.path, segments.slice(0, i).join(node_path_1.sep));
|
|
333
428
|
try {
|
|
334
429
|
yield fs.promises.stat(subPath);
|
|
335
430
|
}
|
|
336
|
-
catch (
|
|
431
|
+
catch (_a) {
|
|
337
432
|
yield fs.promises.mkdir(subPath);
|
|
338
433
|
}
|
|
339
434
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the URL for a Maven artifact JAR in a repository.
|
|
3
|
+
*/
|
|
4
|
+
export declare function buildMavenArtifactUrl(groupId: string, artifactId: string, version: string, classifier: string | undefined, repoUrl: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Check if a Maven artifact exists in a repository by sending an HTTP HEAD request.
|
|
7
|
+
* Returns true if the server responds with a 2xx status, false otherwise.
|
|
8
|
+
*/
|
|
9
|
+
export declare function artifactExistsInRepo(groupId: string, artifactId: string, version: string, classifier: string | undefined, repoUrl: string, headers?: Record<string, string>): Promise<boolean>;
|
|
10
|
+
export interface IModpackMod {
|
|
11
|
+
type: 'maven';
|
|
12
|
+
artifact: string;
|
|
13
|
+
repo: string;
|
|
14
|
+
headers?: Record<string, string>;
|
|
15
|
+
}
|
|
16
|
+
export interface IModpackJson {
|
|
17
|
+
minecraft: string;
|
|
18
|
+
neoforge?: string;
|
|
19
|
+
forge?: string;
|
|
20
|
+
mods: IModpackMod[];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Convert a Maven pom.xml (and optional settings.xml) into a modpack.json object.
|
|
24
|
+
* @param pomXml The contents of the pom.xml file.
|
|
25
|
+
* @param settingsXml The contents of the settings.xml file, or undefined.
|
|
26
|
+
* @returns The generated modpack.json object.
|
|
27
|
+
*/
|
|
28
|
+
export declare function convertPomToModpack(pomXml: string, settingsXml: string | undefined): Promise<IModpackJson>;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.buildMavenArtifactUrl = buildMavenArtifactUrl;
|
|
16
|
+
exports.artifactExistsInRepo = artifactExistsInRepo;
|
|
17
|
+
exports.convertPomToModpack = convertPomToModpack;
|
|
18
|
+
const node_util_1 = require("node:util");
|
|
19
|
+
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
20
|
+
const xml2js_1 = require("xml2js");
|
|
21
|
+
const parseStringPromise = (0, node_util_1.promisify)(xml2js_1.parseString);
|
|
22
|
+
/** Default Maven repository URL, used when no settings.xml is provided. */
|
|
23
|
+
const DEFAULT_MAVEN_REPO = 'https://repo.maven.apache.org/maven2';
|
|
24
|
+
/**
|
|
25
|
+
* Build the URL for a Maven artifact JAR in a repository.
|
|
26
|
+
*/
|
|
27
|
+
function buildMavenArtifactUrl(groupId, artifactId, version, classifier, repoUrl) {
|
|
28
|
+
const groupPath = groupId.replaceAll('.', '/');
|
|
29
|
+
const suffix = classifier ? `-${classifier}` : '';
|
|
30
|
+
const fileName = `${artifactId}-${version}${suffix}.jar`;
|
|
31
|
+
const base = repoUrl.endsWith('/') ? repoUrl : `${repoUrl}/`;
|
|
32
|
+
return `${base}${groupPath}/${artifactId}/${version}/${fileName}`;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Check if a Maven artifact exists in a repository by sending an HTTP HEAD request.
|
|
36
|
+
* Returns true if the server responds with a 2xx status, false otherwise.
|
|
37
|
+
*/
|
|
38
|
+
function artifactExistsInRepo(groupId, artifactId, version, classifier, repoUrl, headers) {
|
|
39
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
40
|
+
const url = buildMavenArtifactUrl(groupId, artifactId, version, classifier, repoUrl);
|
|
41
|
+
try {
|
|
42
|
+
const response = yield (0, node_fetch_1.default)(url, { method: 'HEAD', headers: headers !== null && headers !== void 0 ? headers : {} });
|
|
43
|
+
return response.ok;
|
|
44
|
+
}
|
|
45
|
+
catch (_a) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Build Authorization headers for a server entry.
|
|
52
|
+
* Returns a Bearer header for placeholder passwords, or a Basic header for literal credentials.
|
|
53
|
+
* Returns an empty object if no password is configured.
|
|
54
|
+
*/
|
|
55
|
+
function buildAuthHeaders(server) {
|
|
56
|
+
var _a;
|
|
57
|
+
if (!(server === null || server === void 0 ? void 0 : server.password)) {
|
|
58
|
+
return {};
|
|
59
|
+
}
|
|
60
|
+
const headers = {};
|
|
61
|
+
// If the password is an environment variable placeholder (e.g. ${GITHUB_TOKEN}),
|
|
62
|
+
// use Bearer authentication to avoid having to base64-encode at generation time.
|
|
63
|
+
if (/^\$\{[^}]+\}$/u.test(server.password)) {
|
|
64
|
+
headers.Authorization = `Bearer ${server.password}`;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
// Literal credentials: encode as Basic auth
|
|
68
|
+
const credentials = Buffer
|
|
69
|
+
.from(`${(_a = server.username) !== null && _a !== void 0 ? _a : 'token'}:${server.password}`)
|
|
70
|
+
.toString('base64');
|
|
71
|
+
headers.Authorization = `Basic ${credentials}`;
|
|
72
|
+
}
|
|
73
|
+
return headers;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Convert a Maven pom.xml (and optional settings.xml) into a modpack.json object.
|
|
77
|
+
* @param pomXml The contents of the pom.xml file.
|
|
78
|
+
* @param settingsXml The contents of the settings.xml file, or undefined.
|
|
79
|
+
* @returns The generated modpack.json object.
|
|
80
|
+
*/
|
|
81
|
+
function convertPomToModpack(pomXml, settingsXml) {
|
|
82
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
83
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
84
|
+
const pomDoc = (yield parseStringPromise(pomXml));
|
|
85
|
+
const project = pomDoc.project;
|
|
86
|
+
// Extract Minecraft version from the project <version>
|
|
87
|
+
const minecraftVersion = project.version[0];
|
|
88
|
+
// Extract NeoForge/Forge version from <properties>
|
|
89
|
+
let neoforgeVersion;
|
|
90
|
+
let forgeVersion;
|
|
91
|
+
if ((_a = project.properties) === null || _a === void 0 ? void 0 : _a[0]) {
|
|
92
|
+
const props = project.properties[0];
|
|
93
|
+
if (props['neoforge.version']) {
|
|
94
|
+
neoforgeVersion = props['neoforge.version'][0];
|
|
95
|
+
}
|
|
96
|
+
if (props['forge.version']) {
|
|
97
|
+
forgeVersion = props['forge.version'][0];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Parse settings.xml if provided
|
|
101
|
+
const activeProfileIds = new Set();
|
|
102
|
+
const repoMap = new Map();
|
|
103
|
+
const serverMap = new Map();
|
|
104
|
+
if (settingsXml) {
|
|
105
|
+
const settingsDoc = (yield parseStringPromise(settingsXml));
|
|
106
|
+
const settings = settingsDoc.settings;
|
|
107
|
+
// Collect active profile IDs
|
|
108
|
+
if ((_c = (_b = settings.activeProfiles) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.activeProfile) {
|
|
109
|
+
for (const id of settings.activeProfiles[0].activeProfile) {
|
|
110
|
+
activeProfileIds.add(id);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Collect repositories from active profiles
|
|
114
|
+
if ((_e = (_d = settings.profiles) === null || _d === void 0 ? void 0 : _d[0]) === null || _e === void 0 ? void 0 : _e.profile) {
|
|
115
|
+
for (const profile of settings.profiles[0].profile) {
|
|
116
|
+
const profileId = profile.id[0];
|
|
117
|
+
if (!activeProfileIds.has(profileId)) {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if ((_g = (_f = profile.repositories) === null || _f === void 0 ? void 0 : _f[0]) === null || _g === void 0 ? void 0 : _g.repository) {
|
|
121
|
+
for (const repo of profile.repositories[0].repository) {
|
|
122
|
+
const repoId = repo.id[0];
|
|
123
|
+
const repoUrl = repo.url[0].replace(/\/$/u, '');
|
|
124
|
+
repoMap.set(repoId, { id: repoId, url: repoUrl });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Collect server credentials
|
|
130
|
+
if ((_j = (_h = settings.servers) === null || _h === void 0 ? void 0 : _h[0]) === null || _j === void 0 ? void 0 : _j.server) {
|
|
131
|
+
for (const server of settings.servers[0].server) {
|
|
132
|
+
const serverId = server.id[0];
|
|
133
|
+
const username = (_k = server.username) === null || _k === void 0 ? void 0 : _k[0];
|
|
134
|
+
const password = (_l = server.password) === null || _l === void 0 ? void 0 : _l[0];
|
|
135
|
+
serverMap.set(serverId, { id: serverId, username, password });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// Collect dependencies from pom.xml
|
|
140
|
+
const mods = [];
|
|
141
|
+
if ((_o = (_m = project.dependencies) === null || _m === void 0 ? void 0 : _m[0]) === null || _o === void 0 ? void 0 : _o.dependency) {
|
|
142
|
+
const repos = [...repoMap.values()];
|
|
143
|
+
for (const dep of project.dependencies[0].dependency) {
|
|
144
|
+
const groupId = dep.groupId[0];
|
|
145
|
+
const artifactId = dep.artifactId[0];
|
|
146
|
+
const version = dep.version[0];
|
|
147
|
+
const classifier = (_p = dep.classifier) === null || _p === void 0 ? void 0 : _p[0];
|
|
148
|
+
const artifact = classifier ?
|
|
149
|
+
`${groupId}:${artifactId}:${version}:${classifier}` :
|
|
150
|
+
`${groupId}:${artifactId}:${version}`;
|
|
151
|
+
// Determine repo: when multiple repos are configured, probe each one in order
|
|
152
|
+
// and use the first that has the artifact (mirrors Maven's repository resolution).
|
|
153
|
+
// When only one repo is configured, skip probing and use it directly.
|
|
154
|
+
let repo = (_q = repos[0]) !== null && _q !== void 0 ? _q : { id: 'central', url: DEFAULT_MAVEN_REPO };
|
|
155
|
+
if (repos.length > 1) {
|
|
156
|
+
for (const candidateRepo of repos) {
|
|
157
|
+
const candidateServer = serverMap.get(candidateRepo.id);
|
|
158
|
+
const probeHeaders = buildAuthHeaders(candidateServer);
|
|
159
|
+
const found = yield artifactExistsInRepo(groupId, artifactId, version, classifier, candidateRepo.url, probeHeaders);
|
|
160
|
+
if (found) {
|
|
161
|
+
repo = candidateRepo;
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const mod = {
|
|
167
|
+
type: 'maven',
|
|
168
|
+
artifact,
|
|
169
|
+
repo: repo.url,
|
|
170
|
+
};
|
|
171
|
+
// If the repo has a server entry with credentials, add auth headers
|
|
172
|
+
const server = serverMap.get(repo.id);
|
|
173
|
+
if (server === null || server === void 0 ? void 0 : server.password) {
|
|
174
|
+
mod.headers = buildAuthHeaders(server);
|
|
175
|
+
}
|
|
176
|
+
mods.push(mod);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// Build the modpack.json output
|
|
180
|
+
const modpack = {
|
|
181
|
+
minecraft: minecraftVersion,
|
|
182
|
+
mods,
|
|
183
|
+
};
|
|
184
|
+
if (neoforgeVersion) {
|
|
185
|
+
modpack.neoforge = neoforgeVersion;
|
|
186
|
+
}
|
|
187
|
+
else if (forgeVersion) {
|
|
188
|
+
modpack.forge = forgeVersion;
|
|
189
|
+
}
|
|
190
|
+
// Reorder so minecraft/neoforge/forge come before mods
|
|
191
|
+
return Object.assign(Object.assign(Object.assign({ minecraft: modpack.minecraft }, (modpack.neoforge ? { neoforge: modpack.neoforge } : {})), (modpack.forge ? { forge: modpack.forge } : {})), { mods: modpack.mods });
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=PomConverter.js.map
|