cyclops-infobook-html 5.1.0 → 5.2.0

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.
Files changed (52) hide show
  1. package/README.md +12 -12
  2. package/bin/compress-icons.js +42 -7
  3. package/bin/convert-pom-to-modpack.d.ts +2 -0
  4. package/bin/convert-pom-to-modpack.js +96 -0
  5. package/bin/generate-icons.js +50 -15
  6. package/bin/generate-infobook-html.js +46 -10
  7. package/bin/generate-mod-metadata.js +52 -14
  8. package/index.d.ts +25 -25
  9. package/lib/icon/IconsCompressor.js +43 -12
  10. package/lib/icon/IconsGenerator.d.ts +42 -20
  11. package/lib/icon/IconsGenerator.js +156 -70
  12. package/lib/infobook/FileWriter.d.ts +3 -3
  13. package/lib/infobook/FileWriter.js +5 -5
  14. package/lib/infobook/IFileWriter.d.ts +2 -2
  15. package/lib/infobook/IInfoAppendix.d.ts +3 -3
  16. package/lib/infobook/IInfoBook.d.ts +2 -4
  17. package/lib/infobook/IInfoSection.d.ts +1 -1
  18. package/lib/infobook/IInfobookPlugin.d.ts +5 -5
  19. package/lib/infobook/InfoBookInitializer.d.ts +7 -9
  20. package/lib/infobook/InfoBookInitializer.js +1 -1
  21. package/lib/infobook/appendix/IInfoBookAppendixHandler.d.ts +2 -2
  22. package/lib/infobook/appendix/InfoBookAppendixAd.d.ts +3 -3
  23. package/lib/infobook/appendix/InfoBookAppendixAd.js +7 -2
  24. package/lib/infobook/appendix/InfoBookAppendixHandlerAbstractRecipe.d.ts +12 -14
  25. package/lib/infobook/appendix/InfoBookAppendixHandlerAbstractRecipe.js +41 -8
  26. package/lib/infobook/appendix/InfoBookAppendixHandlerAdvancementRewards.d.ts +3 -3
  27. package/lib/infobook/appendix/InfoBookAppendixHandlerAdvancementRewards.js +5 -4
  28. package/lib/infobook/appendix/InfoBookAppendixHandlerCraftingRecipe.d.ts +6 -5
  29. package/lib/infobook/appendix/InfoBookAppendixHandlerCraftingRecipe.js +4 -3
  30. package/lib/infobook/appendix/InfoBookAppendixHandlerImage.d.ts +3 -3
  31. package/lib/infobook/appendix/InfoBookAppendixHandlerImage.js +4 -4
  32. package/lib/infobook/appendix/InfoBookAppendixHandlerKeybinding.d.ts +3 -3
  33. package/lib/infobook/appendix/InfoBookAppendixHandlerKeybinding.js +3 -2
  34. package/lib/infobook/appendix/InfoBookAppendixHandlerSmeltingRecipe.d.ts +6 -5
  35. package/lib/infobook/appendix/InfoBookAppendixHandlerSmeltingRecipe.js +3 -2
  36. package/lib/infobook/appendix/InfoBookAppendixHandlerTextfield.d.ts +3 -3
  37. package/lib/infobook/appendix/InfoBookAppendixHandlerTextfield.js +4 -5
  38. package/lib/infobook/appendix/InfoBookAppendixTagIndex.d.ts +4 -4
  39. package/lib/infobook/appendix/InfoBookAppendixTagIndex.js +2 -1
  40. package/lib/modloader/ModLoader.d.ts +13 -2
  41. package/lib/modloader/ModLoader.js +164 -69
  42. package/lib/modloader/PomConverter.d.ts +19 -0
  43. package/lib/modloader/PomConverter.js +138 -0
  44. package/lib/parse/XmlInfoBookParser.d.ts +5 -7
  45. package/lib/parse/XmlInfoBookParser.js +42 -9
  46. package/lib/resource/ResourceHandler.d.ts +7 -13
  47. package/lib/resource/ResourceHandler.js +16 -11
  48. package/lib/resource/ResourceLoader.d.ts +4 -5
  49. package/lib/resource/ResourceLoader.js +35 -33
  50. package/lib/serialize/HtmlInfoBookSerializer.d.ts +10 -16
  51. package/lib/serialize/HtmlInfoBookSerializer.js +83 -80
  52. 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) {
@@ -10,11 +43,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
43
  };
11
44
  Object.defineProperty(exports, "__esModule", { value: true });
12
45
  exports.IconsCompressor = void 0;
13
- const child_process_1 = require("child_process");
14
- const fs = require("fs");
15
- const path_1 = require("path");
16
- const util_1 = require("util");
17
- const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
46
+ const node_child_process_1 = require("node:child_process");
47
+ const fs = __importStar(require("node:fs"));
48
+ const node_path_1 = require("node:path");
49
+ const node_util_1 = require("node:util");
50
+ const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
18
51
  /**
19
52
  * Losslessly compresses PNG icon files using OptiPNG.
20
53
  * Mimics how ImgBot compresses images: lossless PNG compression at optimization level 7
@@ -37,7 +70,7 @@ class IconsCompressor {
37
70
  throw new Error(`OptiPNG binary not found at: ${optipngPath}. Ensure optipng-bin is installed.`);
38
71
  }
39
72
  const files = yield fs.promises.readdir(this.iconsDir);
40
- const pngFiles = files.filter((f) => f.endsWith('.png'));
73
+ const pngFiles = files.filter(f => f.endsWith('.png'));
41
74
  if (pngFiles.length === 0) {
42
75
  process.stdout.write(`No PNG files found in ${this.iconsDir}\n`);
43
76
  return;
@@ -47,7 +80,7 @@ class IconsCompressor {
47
80
  let errors = 0;
48
81
  let totalSavedBytes = 0;
49
82
  for (const file of pngFiles) {
50
- const filePath = (0, path_1.join)(this.iconsDir, file);
83
+ const filePath = (0, node_path_1.join)(this.iconsDir, file);
51
84
  const sizeBefore = fs.statSync(filePath).size;
52
85
  try {
53
86
  yield execFileAsync(optipngPath, ['-o7', '-strip', 'all', '-quiet', filePath]);
@@ -61,9 +94,7 @@ class IconsCompressor {
61
94
  }
62
95
  }
63
96
  const savedKb = (totalSavedBytes / 1024).toFixed(1);
64
- process.stdout.write(`Compressed ${compressed} icons (saved ${savedKb} KB)` +
65
- (errors > 0 ? `, ${errors} errors` : '') +
66
- `\n`);
97
+ process.stdout.write(`Compressed ${compressed} icons (saved ${savedKb} KB)${errors > 0 ? `, ${errors} errors` : ''}\n`);
67
98
  });
68
99
  }
69
100
  /**
@@ -72,9 +103,9 @@ class IconsCompressor {
72
103
  */
73
104
  getOptipngPath() {
74
105
  const indexPath = require.resolve('optipng-bin');
75
- const pkgRoot = (0, path_1.dirname)(indexPath);
106
+ const pkgRoot = (0, node_path_1.dirname)(indexPath);
76
107
  const binaryName = process.platform === 'win32' ? 'optipng.exe' : 'optipng';
77
- return (0, path_1.join)(pkgRoot, 'vendor', binaryName);
108
+ return (0, node_path_1.join)(pkgRoot, 'vendor', binaryName);
78
109
  }
79
110
  }
80
111
  exports.IconsCompressor = IconsCompressor;
@@ -5,20 +5,20 @@
5
5
  * runs the iconexporter export command, and copies the resulting icons.
6
6
  */
7
7
  export declare class IconsGenerator {
8
- private static readonly HEADLESSMC_JAR;
9
- private static readonly ICONEXPORTER_JAR;
10
- private static readonly HMC_GAME_SUBDIR;
11
- private static readonly HMC_CONFIG_SUBDIR;
12
- private static readonly ICON_EXPORT_SUBDIR;
13
- private static readonly DEFAULT_ICON_SIZE;
14
- private static readonly HMC_SPECIFICS_REPO;
8
+ private static readonly headlessMcJar;
9
+ private static readonly iconExporterJar;
10
+ private static readonly hmcGameSubdir;
11
+ private static readonly hmcConfigSubdir;
12
+ private static readonly iconExportSubdir;
13
+ private static readonly defaultIconSize;
14
+ private static readonly hmcSpecificsRepo;
15
+ private static readonly modrinthApiBase;
16
+ private static readonly iconExporterModrinthSlug;
15
17
  private readonly modsDir;
16
18
  private readonly iconsDir;
17
19
  private readonly workDir;
18
20
  private readonly minecraftVersion;
19
21
  private readonly neoforgeVersion;
20
- private readonly githubToken;
21
- private readonly iconExporterArtifact;
22
22
  private readonly iconExporterVersion;
23
23
  private readonly headlessMcVersion;
24
24
  private readonly launchTimeoutMs;
@@ -32,9 +32,21 @@ export declare class IconsGenerator {
32
32
  */
33
33
  downloadHeadlessMc(): Promise<void>;
34
34
  /**
35
- * Download the IconExporter mod from GitHub Maven packages.
35
+ * Download the IconExporter mod from Modrinth.
36
+ * If an explicit version is configured, fetches that specific version; otherwise fetches the
37
+ * latest version for the configured Minecraft version. No authentication is required.
36
38
  */
37
39
  downloadIconExporter(): Promise<void>;
40
+ /**
41
+ * Fetch an IconExporter release from Modrinth for the configured Minecraft version and the
42
+ * NeoForge loader. If a specific version number is provided, that version is matched;
43
+ * otherwise the latest (newest) version is returned.
44
+ * Returns the primary file's download URL and filename.
45
+ */
46
+ fetchIconExporterFromModrinth(version?: string): Promise<{
47
+ url: string;
48
+ filename: string;
49
+ }>;
38
50
  /**
39
51
  * Set up the game directory with mods and options.
40
52
  */
@@ -83,6 +95,23 @@ export declare class IconsGenerator {
83
95
  downloadFile(url: string, destPath: string, headers?: Record<string, string>): Promise<void>;
84
96
  }
85
97
  export type IGameState = 'waiting_for_prompt' | 'game_launching' | 'checking_screen' | 'navigating_singleplayer' | 'creating_world' | 'exporting_icons' | 'quitting';
98
+ export interface IModrinthVersionFile {
99
+ url: string;
100
+ filename: string;
101
+ primary: boolean;
102
+ }
103
+ export interface IModrinthVersion {
104
+ version_number: string;
105
+ files: IModrinthVersionFile[];
106
+ }
107
+ export interface IGithubAsset {
108
+ name: string;
109
+ browser_download_url: string;
110
+ }
111
+ export interface IGithubRelease {
112
+ assets: IGithubAsset[];
113
+ tag_name: string;
114
+ }
86
115
  export interface IIconsGeneratorArgs {
87
116
  /**
88
117
  * Directory containing mod JARs to include in the client (usually server/mods).
@@ -105,16 +134,9 @@ export interface IIconsGeneratorArgs {
105
134
  */
106
135
  neoforgeVersion: string;
107
136
  /**
108
- * GitHub token for downloading from GitHub Packages.
109
- * Falls back to GITHUB_TOKEN environment variable.
110
- */
111
- githubToken?: string;
112
- /**
113
- * Maven artifact ID for IconExporter (default: iconexporter-{minecraftVersion}-neoforge).
114
- */
115
- iconExporterArtifact?: string;
116
- /**
117
- * Version of the IconExporter artifact to download (e.g., "1.4.0-174").
137
+ * Version of the IconExporter artifact to pin (e.g., "1.4.0-174").
138
+ * If not provided, the latest version for the configured Minecraft version is
139
+ * automatically fetched from Modrinth (no authentication required).
118
140
  */
119
141
  iconExporterVersion?: string;
120
142
  /**
@@ -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,13 +41,16 @@ 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.IconsGenerator = void 0;
13
- /* tslint:disable:ter-indent */
14
- const child_process_1 = require("child_process");
15
- const fs = require("fs");
16
- const node_fetch_1 = require("node-fetch");
17
- const path_1 = require("path");
49
+ /* Tslint:disable:ter-indent */
50
+ const node_child_process_1 = require("node:child_process");
51
+ const fs = __importStar(require("node:fs"));
52
+ const node_path_1 = require("node:path");
53
+ const node_fetch_1 = __importDefault(require("node-fetch"));
18
54
  /**
19
55
  * Generates icons using the IconExporter mod and HeadlessMC.
20
56
  * This class downloads HeadlessMC and the IconExporter mod, sets up a headless
@@ -40,11 +76,10 @@ class IconsGenerator {
40
76
  this.workDir = args.workDir;
41
77
  this.minecraftVersion = args.minecraftVersion;
42
78
  this.neoforgeVersion = args.neoforgeVersion;
43
- this.githubToken = args.githubToken || process.env.GITHUB_TOKEN || '';
44
- this.iconExporterArtifact = args.iconExporterArtifact || `iconexporter-${args.minecraftVersion}-neoforge`;
45
- this.iconExporterVersion = args.iconExporterVersion || '1.4.0-174';
79
+ this.iconExporterVersion = args.iconExporterVersion;
46
80
  this.headlessMcVersion = args.headlessMcVersion || '2.8.0';
47
- this.launchTimeoutMs = args.launchTimeoutMs || (15 * 60 * 1000); // 15 minutes
81
+ // 15 minutes
82
+ this.launchTimeoutMs = args.launchTimeoutMs || (15 * 60 * 1000);
48
83
  }
49
84
  /**
50
85
  * Run the full icon generation pipeline.
@@ -74,7 +109,7 @@ class IconsGenerator {
74
109
  */
75
110
  downloadHeadlessMc() {
76
111
  return __awaiter(this, void 0, void 0, function* () {
77
- const jarPath = (0, path_1.join)(this.workDir, IconsGenerator.HEADLESSMC_JAR);
112
+ const jarPath = (0, node_path_1.join)(this.workDir, IconsGenerator.headlessMcJar);
78
113
  if (fs.existsSync(jarPath)) {
79
114
  process.stdout.write('HeadlessMC already downloaded, skipping.\n');
80
115
  return;
@@ -85,32 +120,78 @@ class IconsGenerator {
85
120
  });
86
121
  }
87
122
  /**
88
- * Download the IconExporter mod from GitHub Maven packages.
123
+ * Download the IconExporter mod from Modrinth.
124
+ * If an explicit version is configured, fetches that specific version; otherwise fetches the
125
+ * latest version for the configured Minecraft version. No authentication is required.
89
126
  */
90
127
  downloadIconExporter() {
91
128
  return __awaiter(this, void 0, void 0, function* () {
92
- const jarPath = (0, path_1.join)(this.workDir, IconsGenerator.ICONEXPORTER_JAR);
129
+ const jarPath = (0, node_path_1.join)(this.workDir, IconsGenerator.iconExporterJar);
93
130
  if (fs.existsSync(jarPath)) {
94
131
  process.stdout.write('IconExporter already downloaded, skipping.\n');
95
132
  return;
96
133
  }
97
- const groupPath = 'org/cyclops/iconexporter';
98
- const url = `https://maven.pkg.github.com/CyclopsMC/packages/${groupPath}/${this.iconExporterArtifact}/${this.iconExporterVersion}/${this.iconExporterArtifact}-${this.iconExporterVersion}.jar`;
99
- const headers = {};
100
- if (this.githubToken) {
101
- headers.Authorization = `token ${this.githubToken}`;
134
+ if (this.iconExporterVersion) {
135
+ process.stdout.write(`Fetching IconExporter ${this.iconExporterVersion} for Minecraft ${this.minecraftVersion} from Modrinth...\n`);
136
+ }
137
+ else {
138
+ process.stdout.write(`Fetching latest IconExporter version for Minecraft ${this.minecraftVersion} from Modrinth...\n`);
102
139
  }
103
- yield this.downloadFile(url, jarPath, headers);
140
+ const { url: downloadUrl, filename } = yield this.fetchIconExporterFromModrinth(this.iconExporterVersion);
141
+ process.stdout.write(`Found IconExporter: ${filename}\n`);
142
+ yield this.downloadFile(downloadUrl, jarPath);
104
143
  process.stdout.write(`Downloaded IconExporter to ${jarPath}\n`);
105
144
  });
106
145
  }
146
+ /**
147
+ * Fetch an IconExporter release from Modrinth for the configured Minecraft version and the
148
+ * NeoForge loader. If a specific version number is provided, that version is matched;
149
+ * otherwise the latest (newest) version is returned.
150
+ * Returns the primary file's download URL and filename.
151
+ */
152
+ fetchIconExporterFromModrinth(version) {
153
+ return __awaiter(this, void 0, void 0, function* () {
154
+ const gameVersions = JSON.stringify([this.minecraftVersion]);
155
+ const loaders = JSON.stringify(['neoforge']);
156
+ const apiUrl = `${IconsGenerator.modrinthApiBase}/project/${IconsGenerator.iconExporterModrinthSlug}/version` +
157
+ `?game_versions=${encodeURIComponent(gameVersions)}&loaders=${encodeURIComponent(loaders)}`;
158
+ const response = yield (0, node_fetch_1.default)(apiUrl);
159
+ if (!response.ok) {
160
+ throw new Error(`Failed to fetch IconExporter versions from Modrinth: ${response.status} ${response.statusText}`);
161
+ }
162
+ const versions = yield response.json();
163
+ if (!versions || versions.length === 0) {
164
+ throw new Error(`No IconExporter versions found on Modrinth for Minecraft ${this.minecraftVersion} with NeoForge`);
165
+ }
166
+ let matched;
167
+ if (version) {
168
+ // Modrinth version_number is typically "{mcVersion}-{modVersion}" (e.g. "1.21.1-1.4.0-174").
169
+ // Match either an exact version_number or the canonical "<mcVersion>-<version>" form.
170
+ const canonicalVersionNumber = `${this.minecraftVersion}-${version}`;
171
+ const found = versions.find(v => v.version_number === version || v.version_number === canonicalVersionNumber);
172
+ if (!found) {
173
+ throw new Error(`IconExporter version "${version}" not found on Modrinth for Minecraft ${this.minecraftVersion} with NeoForge`);
174
+ }
175
+ matched = found;
176
+ }
177
+ else {
178
+ // Modrinth returns versions newest-first
179
+ matched = versions[0];
180
+ }
181
+ const primaryFile = matched.files.find(f => f.primary) || matched.files[0];
182
+ if (!primaryFile) {
183
+ throw new Error(`No files found for IconExporter version ${matched.version_number}`);
184
+ }
185
+ return { url: primaryFile.url, filename: primaryFile.filename };
186
+ });
187
+ }
107
188
  /**
108
189
  * Set up the game directory with mods and options.
109
190
  */
110
191
  setupGameDirectory() {
111
192
  return __awaiter(this, void 0, void 0, function* () {
112
- const gameDir = (0, path_1.join)(this.workDir, IconsGenerator.HMC_GAME_SUBDIR);
113
- const modsDir = (0, path_1.join)(gameDir, 'mods');
193
+ const gameDir = (0, node_path_1.join)(this.workDir, IconsGenerator.hmcGameSubdir);
194
+ const modsDir = (0, node_path_1.join)(gameDir, 'mods');
114
195
  if (!fs.existsSync(modsDir)) {
115
196
  yield fs.promises.mkdir(modsDir, { recursive: true });
116
197
  }
@@ -120,7 +201,7 @@ class IconsGenerator {
120
201
  let copied = 0;
121
202
  for (const mod of mods) {
122
203
  if (mod.endsWith('.jar')) {
123
- yield fs.promises.copyFile((0, path_1.join)(this.modsDir, mod), (0, path_1.join)(modsDir, mod));
204
+ yield fs.promises.copyFile((0, node_path_1.join)(this.modsDir, mod), (0, node_path_1.join)(modsDir, mod));
124
205
  copied++;
125
206
  }
126
207
  }
@@ -130,20 +211,20 @@ class IconsGenerator {
130
211
  process.stdout.write(`Warning: mods directory not found: ${this.modsDir}\n`);
131
212
  }
132
213
  // Copy IconExporter to mods directory
133
- const iconExporterSrc = (0, path_1.join)(this.workDir, IconsGenerator.ICONEXPORTER_JAR);
214
+ const iconExporterSrc = (0, node_path_1.join)(this.workDir, IconsGenerator.iconExporterJar);
134
215
  if (fs.existsSync(iconExporterSrc)) {
135
- yield fs.promises.copyFile(iconExporterSrc, (0, path_1.join)(modsDir, IconsGenerator.ICONEXPORTER_JAR));
216
+ yield fs.promises.copyFile(iconExporterSrc, (0, node_path_1.join)(modsDir, IconsGenerator.iconExporterJar));
136
217
  process.stdout.write('Added IconExporter to mods directory\n');
137
218
  }
138
219
  // Download hmc-specifics if not already present (avoids relying on HeadlessMC's
139
220
  // GitHub API lookup which may hit rate limits in CI environments)
140
221
  const existingMods = yield fs.promises.readdir(modsDir);
141
- const hasHmcSpecifics = existingMods.some((f) => f.startsWith('hmc-specifics-'));
222
+ const hasHmcSpecifics = existingMods.some(f => f.startsWith('hmc-specifics-'));
142
223
  if (!hasHmcSpecifics) {
143
224
  yield this.downloadHmcSpecifics(modsDir);
144
225
  }
145
226
  // Write options.txt to disable accessibility screen and pauseOnLostFocus
146
- const optionsPath = (0, path_1.join)(gameDir, 'options.txt');
227
+ const optionsPath = (0, node_path_1.join)(gameDir, 'options.txt');
147
228
  if (!fs.existsSync(optionsPath)) {
148
229
  yield fs.promises.writeFile(optionsPath, 'pauseOnLostFocus:false\nonboardAccessibility:false\n');
149
230
  }
@@ -153,12 +234,12 @@ class IconsGenerator {
153
234
  * Write the HeadlessMC configuration file.
154
235
  */
155
236
  writeHmcConfig() {
156
- const configDir = (0, path_1.join)(this.workDir, IconsGenerator.HMC_CONFIG_SUBDIR);
237
+ const configDir = (0, node_path_1.join)(this.workDir, IconsGenerator.hmcConfigSubdir);
157
238
  if (!fs.existsSync(configDir)) {
158
239
  fs.mkdirSync(configDir, { recursive: true });
159
240
  }
160
- const gameDir = (0, path_1.join)(this.workDir, IconsGenerator.HMC_GAME_SUBDIR);
161
- const configPath = (0, path_1.join)(configDir, 'config.properties');
241
+ const gameDir = (0, node_path_1.join)(this.workDir, IconsGenerator.hmcGameSubdir);
242
+ const configPath = (0, node_path_1.join)(configDir, 'config.properties');
162
243
  const config = [
163
244
  `hmc.mcdir=${gameDir}`,
164
245
  'hmc.jline.enabled=false',
@@ -171,19 +252,23 @@ class IconsGenerator {
171
252
  */
172
253
  runGameAndExportIcons() {
173
254
  return __awaiter(this, void 0, void 0, function* () {
174
- const jarPath = (0, path_1.join)(this.workDir, IconsGenerator.HEADLESSMC_JAR);
255
+ const jarPath = (0, node_path_1.join)(this.workDir, IconsGenerator.headlessMcJar);
175
256
  return new Promise((resolve, reject) => {
176
- const proc = (0, child_process_1.spawn)('java', [
177
- `-Dhmc.mcdir=${(0, path_1.join)(this.workDir, IconsGenerator.HMC_GAME_SUBDIR)}`,
257
+ const proc = (0, node_child_process_1.spawn)('java', [
258
+ `-Dhmc.mcdir=${(0, node_path_1.join)(this.workDir, IconsGenerator.hmcGameSubdir)}`,
178
259
  // Tell HeadlessMC that Xvfb is in use so it skips offline headless checks
179
260
  '-Dhmc.check.xvfb=true',
180
- '-jar', jarPath,
261
+ '-jar',
262
+ jarPath,
181
263
  ], {
182
264
  cwd: this.workDir,
183
265
  stdio: ['pipe', 'pipe', 'pipe'],
184
266
  env: Object.assign(Object.assign({}, process.env), {
185
267
  // Disable ANSI escape sequences for easier parsing
186
- TERM: 'dumb', NO_COLOR: '1' }),
268
+ // eslint-disable-next-line ts/naming-convention
269
+ TERM: 'dumb',
270
+ // eslint-disable-next-line ts/naming-convention
271
+ NO_COLOR: '1' }),
187
272
  });
188
273
  let outputBuffer = '';
189
274
  // Per-state buffer: cleared on every state transition, so checks only match
@@ -196,7 +281,7 @@ class IconsGenerator {
196
281
  let commandSent = false;
197
282
  const sendCommand = (command) => {
198
283
  process.stdout.write(`[HMC] > ${command}\n`);
199
- proc.stdin.write(command + '\n');
284
+ proc.stdin.write(`${command}\n`);
200
285
  };
201
286
  // Click a button by its text label, resolving to numeric ID from the last gui output.
202
287
  // HeadlessMC's click command requires the numeric id, not button text.
@@ -206,13 +291,13 @@ class IconsGenerator {
206
291
  const lastScreenIdx = outputBuffer.lastIndexOf('\nScreen:');
207
292
  const recentGuiOutput = lastScreenIdx >= 0 ? outputBuffer.slice(lastScreenIdx) : outputBuffer;
208
293
  const id = this.findButtonIdByText(recentGuiOutput, buttonText);
209
- if (id !== null) {
210
- sendCommand(`click ${id}`);
211
- }
212
- else {
294
+ if (id === null) {
213
295
  process.stdout.write(`[HMC] Warning: button "${buttonText}" not found in gui output, trying text fallback\n`);
214
296
  sendCommand(`click ${buttonText}`);
215
297
  }
298
+ else {
299
+ sendCommand(`click ${id}`);
300
+ }
216
301
  };
217
302
  const transitionTo = (newState) => {
218
303
  if (state !== newState) {
@@ -220,7 +305,8 @@ class IconsGenerator {
220
305
  state = newState;
221
306
  stateSettledAt = Date.now();
222
307
  commandSent = false;
223
- stateBuffer = ''; // reset per-state buffer on every transition
308
+ // Reset per-state buffer on every transition
309
+ stateBuffer = '';
224
310
  }
225
311
  };
226
312
  const handleOutput = (chunk) => {
@@ -228,10 +314,10 @@ class IconsGenerator {
228
314
  stateBuffer += chunk;
229
315
  // Keep global buffer size manageable
230
316
  if (outputBuffer.length > 100000) {
231
- outputBuffer = outputBuffer.slice(outputBuffer.length - 20000);
317
+ outputBuffer = outputBuffer.slice(-20000);
232
318
  }
233
319
  // Track the current screen from each gui output chunk
234
- const screenMatch = chunk.match(/^Screen:\s*(.+)$/m);
320
+ const screenMatch = /^Screen:\s*(.+)$/mu.exec(chunk);
235
321
  if (screenMatch) {
236
322
  lastKnownScreen = screenMatch[1].trim();
237
323
  process.stdout.write(`[HMC] Current screen: ${lastKnownScreen}\n`);
@@ -296,8 +382,8 @@ class IconsGenerator {
296
382
  clickByText('Create New World');
297
383
  transitionTo('creating_world');
298
384
  }
299
- else if (stateBuffer.includes("Couldn't find command for '[gui]'") && !commandSent) {
300
- // gui not yet recognized; hmc-specifics may still be initializing - retry
385
+ else if (stateBuffer.includes('Couldn\'t find command for \'[gui]\'') && !commandSent) {
386
+ // Gui not yet recognized; hmc-specifics may still be initializing - retry
301
387
  commandSent = true;
302
388
  setTimeout(() => {
303
389
  sendCommand('gui');
@@ -325,11 +411,12 @@ class IconsGenerator {
325
411
  clickByText('Create New World');
326
412
  transitionTo('creating_world');
327
413
  }
328
- else if ((stateBuffer.includes('logged in') || stateBuffer.includes('not displaying a Gui')) && !commandSent) {
414
+ else if ((stateBuffer.includes('logged in') || stateBuffer.includes('not displaying a Gui')) &&
415
+ !commandSent) {
329
416
  // World already loaded (detected either from HMC login message or from a gui poll)
330
417
  commandSent = true;
331
418
  setTimeout(() => {
332
- sendCommand(`/iconexporter export ${IconsGenerator.DEFAULT_ICON_SIZE}`);
419
+ sendCommand(`/iconexporter export ${IconsGenerator.defaultIconSize}`);
333
420
  transitionTo('exporting_icons');
334
421
  }, 3000);
335
422
  }
@@ -367,7 +454,7 @@ class IconsGenerator {
367
454
  commandSent = true;
368
455
  // Wait for world to fully initialize before triggering export
369
456
  setTimeout(() => {
370
- sendCommand(`/iconexporter export ${IconsGenerator.DEFAULT_ICON_SIZE}`);
457
+ sendCommand(`/iconexporter export ${IconsGenerator.defaultIconSize}`);
371
458
  transitionTo('exporting_icons');
372
459
  }, 3000);
373
460
  }
@@ -383,7 +470,7 @@ class IconsGenerator {
383
470
  else if (Date.now() - stateSettledAt > 120000 && !commandSent) {
384
471
  // Timeout waiting for world - try to proceed anyway
385
472
  commandSent = true;
386
- sendCommand(`/iconexporter export ${IconsGenerator.DEFAULT_ICON_SIZE}`);
473
+ sendCommand(`/iconexporter export ${IconsGenerator.defaultIconSize}`);
387
474
  transitionTo('exporting_icons');
388
475
  }
389
476
  else if (!commandSent) {
@@ -412,8 +499,9 @@ class IconsGenerator {
412
499
  sendCommand('quit');
413
500
  transitionTo('quitting');
414
501
  }, 5000);
502
+ // 20 minutes
415
503
  }
416
- else if (Date.now() - stateSettledAt > 1200000) { // 20 minutes
504
+ else if (Date.now() - stateSettledAt > 1200000) {
417
505
  // Timeout - quit anyway
418
506
  commandSent = true;
419
507
  process.stdout.write('[HMC] Warning: icon export may not have completed, quitting...\n');
@@ -473,7 +561,7 @@ class IconsGenerator {
473
561
  */
474
562
  copyIcons() {
475
563
  return __awaiter(this, void 0, void 0, function* () {
476
- const exportDir = (0, path_1.join)(this.workDir, IconsGenerator.HMC_GAME_SUBDIR, IconsGenerator.ICON_EXPORT_SUBDIR);
564
+ const exportDir = (0, node_path_1.join)(this.workDir, IconsGenerator.hmcGameSubdir, IconsGenerator.iconExportSubdir);
477
565
  if (!fs.existsSync(exportDir)) {
478
566
  throw new Error(`Icon export directory not found: ${exportDir}. Make sure the IconExporter command ran successfully.`);
479
567
  }
@@ -481,9 +569,9 @@ class IconsGenerator {
481
569
  yield fs.promises.mkdir(this.iconsDir, { recursive: true });
482
570
  }
483
571
  const files = yield fs.promises.readdir(exportDir);
484
- const pngFiles = files.filter((f) => f.endsWith('.png'));
572
+ const pngFiles = files.filter(f => f.endsWith('.png'));
485
573
  for (const file of pngFiles) {
486
- yield fs.promises.copyFile((0, path_1.join)(exportDir, file), (0, path_1.join)(this.iconsDir, file));
574
+ yield fs.promises.copyFile((0, node_path_1.join)(exportDir, file), (0, node_path_1.join)(this.iconsDir, file));
487
575
  }
488
576
  process.stdout.write(`Copied ${pngFiles.length} icons to ${this.iconsDir}\n`);
489
577
  });
@@ -499,9 +587,9 @@ class IconsGenerator {
499
587
  */
500
588
  findButtonIdByText(guiOutput, buttonText) {
501
589
  for (const line of guiOutput.split('\n')) {
502
- const cols = line.trim().split(/\s{2,}/);
503
- if (cols.length >= 2 && /^\d+$/.test(cols[0]) && cols[1].trim() === buttonText) {
504
- return parseInt(cols[0], 10);
590
+ const cols = line.trim().split(/\s{2,}/u);
591
+ if (cols.length >= 2 && /^\d+$/u.test(cols[0]) && cols[1].trim() === buttonText) {
592
+ return Number.parseInt(cols[0], 10);
505
593
  }
506
594
  }
507
595
  return null;
@@ -539,21 +627,17 @@ class IconsGenerator {
539
627
  downloadHmcSpecifics(modsDir) {
540
628
  return __awaiter(this, void 0, void 0, function* () {
541
629
  process.stdout.write('Downloading hmc-specifics...\n');
542
- const headers = {};
543
- if (this.githubToken) {
544
- headers.Authorization = `token ${this.githubToken}`;
545
- }
546
- const apiUrl = `https://api.github.com/repos/${IconsGenerator.HMC_SPECIFICS_REPO}/releases/latest`;
547
- const apiResponse = yield (0, node_fetch_1.default)(apiUrl, { headers });
630
+ const apiUrl = `https://api.github.com/repos/${IconsGenerator.hmcSpecificsRepo}/releases/latest`;
631
+ const apiResponse = yield (0, node_fetch_1.default)(apiUrl);
548
632
  if (!apiResponse.ok) {
549
633
  throw new Error(`Failed to fetch hmc-specifics release info: ${apiResponse.status} ${apiResponse.statusText}`);
550
634
  }
551
635
  const release = yield apiResponse.json();
552
- const asset = release.assets.find((a) => a.name.includes(this.minecraftVersion) && a.name.includes('neoforge'));
636
+ const asset = release.assets.find(a => a.name.includes(this.minecraftVersion) && a.name.includes('neoforge'));
553
637
  if (!asset) {
554
638
  throw new Error(`Could not find hmc-specifics asset for MC ${this.minecraftVersion} in release ${release.tag_name}`);
555
639
  }
556
- const destPath = (0, path_1.join)(modsDir, asset.name);
640
+ const destPath = (0, node_path_1.join)(modsDir, asset.name);
557
641
  yield this.downloadFile(asset.browser_download_url, destPath);
558
642
  process.stdout.write(`Downloaded hmc-specifics to ${destPath}\n`);
559
643
  });
@@ -571,7 +655,7 @@ class IconsGenerator {
571
655
  if (!response.ok) {
572
656
  throw new Error(`Failed to download from ${url}: ${response.status} ${response.statusText}`);
573
657
  }
574
- const parentDir = (0, path_1.join)(destPath, '..');
658
+ const parentDir = (0, node_path_1.join)(destPath, '..');
575
659
  if (!fs.existsSync(parentDir)) {
576
660
  yield fs.promises.mkdir(parentDir, { recursive: true });
577
661
  }
@@ -587,11 +671,13 @@ class IconsGenerator {
587
671
  }
588
672
  }
589
673
  exports.IconsGenerator = IconsGenerator;
590
- IconsGenerator.HEADLESSMC_JAR = 'headlessmc-launcher.jar';
591
- IconsGenerator.ICONEXPORTER_JAR = 'iconexporter.jar';
592
- IconsGenerator.HMC_GAME_SUBDIR = 'game';
593
- IconsGenerator.HMC_CONFIG_SUBDIR = 'HeadlessMC';
594
- IconsGenerator.ICON_EXPORT_SUBDIR = 'icon-exports-x64';
595
- IconsGenerator.DEFAULT_ICON_SIZE = 64;
596
- IconsGenerator.HMC_SPECIFICS_REPO = 'headlesshq/hmc-specifics';
674
+ IconsGenerator.headlessMcJar = 'headlessmc-launcher.jar';
675
+ IconsGenerator.iconExporterJar = 'iconexporter.jar';
676
+ IconsGenerator.hmcGameSubdir = 'game';
677
+ IconsGenerator.hmcConfigSubdir = 'HeadlessMC';
678
+ IconsGenerator.iconExportSubdir = 'icon-exports-x64';
679
+ IconsGenerator.defaultIconSize = 64;
680
+ IconsGenerator.hmcSpecificsRepo = 'headlesshq/hmc-specifics';
681
+ IconsGenerator.modrinthApiBase = 'https://api.modrinth.com/v2';
682
+ IconsGenerator.iconExporterModrinthSlug = 'icon-exporter';
597
683
  //# sourceMappingURL=IconsGenerator.js.map
@@ -1,6 +1,6 @@
1
- import { Readable } from "stream";
2
- import { ISerializeContext } from "../serialize/HtmlInfoBookSerializer";
3
- import { IFileWriter } from "./IFileWriter";
1
+ import type { Readable } from 'node:stream';
2
+ import type { ISerializeContext } from '../serialize/HtmlInfoBookSerializer';
3
+ import type { IFileWriter } from './IFileWriter';
4
4
  /**
5
5
  * A context-based {@link IFileWriter}.
6
6
  */