modpack-lock 0.5.1 → 0.6.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.
- package/package.json +1 -1
- package/src/cli.js +121 -120
- package/src/config/api.js +5 -5
- package/src/config/constants.js +11 -11
- package/src/config/defaults.js +42 -42
- package/src/config/index.js +6 -6
- package/src/config/options.js +7 -5
- package/src/config/strings.js +38 -41
- package/src/config/types.js +3 -2
- package/src/directory_scanning.js +15 -14
- package/src/generate_gitignore.js +113 -0
- package/src/generate_json.js +40 -39
- package/src/generate_license.js +25 -21
- package/src/generate_lockfile.js +68 -361
- package/src/generate_readme.js +224 -0
- package/src/github_interactions.js +17 -23
- package/src/logger.js +178 -0
- package/src/modpack-lock.js +47 -11
- package/src/modpack_info.js +102 -117
- package/src/modrinth_interactions.js +21 -17
package/src/generate_lockfile.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import fs from
|
|
2
|
-
import path from
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import * as config from
|
|
1
|
+
import fs from "fs/promises";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import {getVersionsFromHashes} from "./modrinth_interactions.js";
|
|
4
|
+
import {getScanDirectories, scanDirectory} from "./directory_scanning.js";
|
|
5
|
+
import * as config from "./config/index.js";
|
|
6
|
+
import {logm, styleText} from "./logger.js";
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* @typedef {import('./config/types.js').Options} Options
|
|
@@ -29,6 +30,7 @@ function createEmptyLockfile() {
|
|
|
29
30
|
function createLockfile(fileEntries, versionData) {
|
|
30
31
|
const lockfile = createEmptyLockfile();
|
|
31
32
|
|
|
33
|
+
logm.newline();
|
|
32
34
|
// Organize by category
|
|
33
35
|
for (const fileInfo of fileEntries) {
|
|
34
36
|
const version = versionData[fileInfo.hash];
|
|
@@ -41,12 +43,14 @@ function createLockfile(fileEntries, versionData) {
|
|
|
41
43
|
};
|
|
42
44
|
|
|
43
45
|
if (!version) {
|
|
44
|
-
|
|
46
|
+
logm.warn(`File ${fileInfo.path} not found on Modrinth`);
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
lockfile.dependencies[fileInfo.category].push(entry);
|
|
48
50
|
}
|
|
49
51
|
|
|
52
|
+
logm.header("Generating Lockfile");
|
|
53
|
+
|
|
50
54
|
// Calculate counts for each category
|
|
51
55
|
for (const [category, entries] of Object.entries(lockfile.dependencies)) {
|
|
52
56
|
lockfile.counts[category] = entries.length;
|
|
@@ -62,331 +66,8 @@ function createLockfile(fileEntries, versionData) {
|
|
|
62
66
|
*/
|
|
63
67
|
async function writeLockfile(lockfile, outputPath) {
|
|
64
68
|
const content = JSON.stringify(lockfile, null, 2);
|
|
65
|
-
await fs.writeFile(outputPath, content,
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Generate README.md content for a category
|
|
71
|
-
*/
|
|
72
|
-
function generateCategoryReadme(category, entries, projectsMap, usersMap) {
|
|
73
|
-
const categoryTitle = category.charAt(0).toUpperCase() + category.slice(1);
|
|
74
|
-
const lines = [`# ${categoryTitle}`, '', '| Name | Author | Version | Dependencies | Dependants |', '|-|-|-|-|-|'];
|
|
75
|
-
|
|
76
|
-
// Map category to Modrinth URL path segment
|
|
77
|
-
const categoryPathMap = {};
|
|
78
|
-
for (const category of config.DEPENDENCY_CATEGORIES) {
|
|
79
|
-
categoryPathMap[category] = category === 'shaderpacks' ? 'shader' : category.toLowerCase().slice(0, -1);
|
|
80
|
-
}
|
|
81
|
-
const categoryPath = categoryPathMap[category] || 'project';
|
|
82
|
-
|
|
83
|
-
// Build a set of project_ids present in this category for filtering dependencies
|
|
84
|
-
const categoryProjectIds = new Set();
|
|
85
|
-
for (const entry of entries) {
|
|
86
|
-
if (entry.version && entry.version.project_id) {
|
|
87
|
-
categoryProjectIds.add(entry.version.project_id);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
for (const entry of entries) {
|
|
92
|
-
const version = entry.version;
|
|
93
|
-
let nameCell = '';
|
|
94
|
-
let authorCell = '';
|
|
95
|
-
let versionCell = '';
|
|
96
|
-
let dependenciesCell = '';
|
|
97
|
-
let dependantsCell = '';
|
|
98
|
-
|
|
99
|
-
if (version && version.project_id) {
|
|
100
|
-
const project = projectsMap[version.project_id];
|
|
101
|
-
const author = version.author_id ? usersMap[version.author_id] : null;
|
|
102
|
-
|
|
103
|
-
// Name column with icon and link
|
|
104
|
-
if (project) {
|
|
105
|
-
const projectName = project.title || project.slug || 'Unknown';
|
|
106
|
-
const projectSlug = project.slug || project.id;
|
|
107
|
-
const projectUrl = `https://modrinth.com/${categoryPath}/${projectSlug}`;
|
|
108
|
-
|
|
109
|
-
if (project.icon_url) {
|
|
110
|
-
nameCell = `<img alt="Icon" src="${project.icon_url}" height="20px"> [${projectName}](${projectUrl})`;
|
|
111
|
-
} else {
|
|
112
|
-
nameCell = `[${projectName}](${projectUrl})`;
|
|
113
|
-
}
|
|
114
|
-
} else {
|
|
115
|
-
// Project not found, use filename
|
|
116
|
-
const fileName = path.basename(entry.path);
|
|
117
|
-
nameCell = fileName;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Author column with avatar and link
|
|
121
|
-
if (author) {
|
|
122
|
-
const authorName = author.username || 'Unknown';
|
|
123
|
-
const authorUrl = `https://modrinth.com/user/${authorName}`;
|
|
124
|
-
|
|
125
|
-
if (author.avatar_url) {
|
|
126
|
-
authorCell = `<img alt="Avatar" src="${author.avatar_url}" height="20px"> [${authorName}](${authorUrl})`;
|
|
127
|
-
} else {
|
|
128
|
-
authorCell = `[${authorName}](${authorUrl})`;
|
|
129
|
-
}
|
|
130
|
-
} else {
|
|
131
|
-
authorCell = 'Unknown';
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Version column
|
|
135
|
-
versionCell = version.version_number || 'Unknown';
|
|
136
|
-
|
|
137
|
-
// Dependencies column - only show dependencies that are present in this category
|
|
138
|
-
if (version.dependencies && Array.isArray(version.dependencies) && version.dependencies.length > 0) {
|
|
139
|
-
const dependencyLinks = [];
|
|
140
|
-
for (const dep of version.dependencies) {
|
|
141
|
-
if (dep.project_id && categoryProjectIds.has(dep.project_id)) {
|
|
142
|
-
const depProject = projectsMap[dep.project_id];
|
|
143
|
-
if (depProject) {
|
|
144
|
-
const depProjectName = depProject.title || depProject.slug || 'Unknown';
|
|
145
|
-
const depProjectSlug = depProject.slug || depProject.id;
|
|
146
|
-
const depUrl = `https://modrinth.com/${categoryPath}/${depProjectSlug}`;
|
|
147
|
-
if (depProject.icon_url) {
|
|
148
|
-
dependencyLinks.push(`<a href="${depUrl}"><img alt="${depProjectName}" src="${depProject.icon_url}" height="20px"></a>`);
|
|
149
|
-
} else {
|
|
150
|
-
dependencyLinks.push(`[${depProjectName}](${depUrl})`);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
dependenciesCell = dependencyLinks.length > 0 ? dependencyLinks.join(' ') : '-';
|
|
156
|
-
} else {
|
|
157
|
-
dependenciesCell = '-';
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Dependants column - find all entries in the same category that depend on this project
|
|
161
|
-
const dependants = [];
|
|
162
|
-
for (const catEntry of entries) {
|
|
163
|
-
// Skip if this is the same entry (same project_id)
|
|
164
|
-
if (catEntry.version && catEntry.version.project_id === version.project_id) {
|
|
165
|
-
continue;
|
|
166
|
-
}
|
|
167
|
-
if (catEntry.version && catEntry.version.dependencies && Array.isArray(catEntry.version.dependencies)) {
|
|
168
|
-
const hasDependency = catEntry.version.dependencies.some(
|
|
169
|
-
dep => dep.project_id === version.project_id
|
|
170
|
-
);
|
|
171
|
-
if (hasDependency) {
|
|
172
|
-
const depProject = projectsMap[catEntry.version.project_id];
|
|
173
|
-
if (depProject) {
|
|
174
|
-
const depProjectName = depProject.title || depProject.slug || 'Unknown';
|
|
175
|
-
const depProjectSlug = depProject.slug || depProject.id;
|
|
176
|
-
const depUrl = `https://modrinth.com/${categoryPath}/${depProjectSlug}`;
|
|
177
|
-
if (depProject.icon_url) {
|
|
178
|
-
dependants.push(`<a href="${depUrl}"><img alt="${depProjectName}" src="${depProject.icon_url}" height="20px"></a>`);
|
|
179
|
-
} else {
|
|
180
|
-
dependants.push(`[${depProjectName}](${depUrl})`);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
dependantsCell = dependants.length > 0 ? dependants.join(' ') : '-';
|
|
187
|
-
} else {
|
|
188
|
-
// File not found on Modrinth
|
|
189
|
-
const fileName = path.basename(entry.path);
|
|
190
|
-
nameCell = fileName;
|
|
191
|
-
authorCell = 'Unknown';
|
|
192
|
-
versionCell = '-';
|
|
193
|
-
dependenciesCell = '-';
|
|
194
|
-
dependantsCell = '-';
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
lines.push(`| ${nameCell} | ${authorCell} | ${versionCell} | ${dependenciesCell} | ${dependantsCell} |`);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
return lines.join('\n') + '\n';
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Generate .gitignore rules for files not hosted on Modrinth and write them to .gitignore file
|
|
205
|
-
* @param {Lockfile} lockfile - The lockfile object
|
|
206
|
-
* @param {string} workingDir - The working directory
|
|
207
|
-
* @param {Options | InitOptions} options - The options object
|
|
208
|
-
*/
|
|
209
|
-
export async function generateGitignoreRules(lockfile, workingDir, options = {}) {
|
|
210
|
-
const rules = [];
|
|
211
|
-
const exceptions = [];
|
|
212
|
-
|
|
213
|
-
// Base ignore patterns for each category
|
|
214
|
-
for (const category of config.DEPENDENCY_CATEGORIES) {
|
|
215
|
-
rules.push(`${category}/*.${category === "mods" ? "jar" : "zip"}`);
|
|
216
|
-
}
|
|
217
|
-
rules.push(`*/**/*.disabled`);
|
|
218
|
-
|
|
219
|
-
// Find files not hosted on Modrinth
|
|
220
|
-
for (const [category, entries] of Object.entries(lockfile.dependencies)) {
|
|
221
|
-
for (const entry of entries) {
|
|
222
|
-
if (entry.version === null) {
|
|
223
|
-
exceptions.push(`!${entry.path}`);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// Add exceptions if any
|
|
229
|
-
if (exceptions.length > 0) {
|
|
230
|
-
rules.push('\n## Exceptions');
|
|
231
|
-
rules.push(...exceptions);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const rulesContent = rules.join('\n');
|
|
235
|
-
const gitignorePath = path.join(workingDir, config.GITIGNORE_NAME);
|
|
236
|
-
|
|
237
|
-
// Read existing .gitignore file if it exists
|
|
238
|
-
let existingContent = '';
|
|
239
|
-
try {
|
|
240
|
-
existingContent = await fs.readFile(gitignorePath, 'utf-8');
|
|
241
|
-
} catch (error) {
|
|
242
|
-
// File doesn't exist, that's okay - we'll create it
|
|
243
|
-
if (error.code !== 'ENOENT') {
|
|
244
|
-
console.warn(`Warning: Could not read .gitignore file: ${error.message}`);
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// Find markers in existing content
|
|
250
|
-
const startMarkerIndex = existingContent.indexOf(config.GITIGNORE_START_MARKER);
|
|
251
|
-
const endMarkerIndex = existingContent.indexOf(config.GITIGNORE_END_MARKER);
|
|
252
|
-
|
|
253
|
-
let newContent = '';
|
|
254
|
-
|
|
255
|
-
if (startMarkerIndex !== -1 && endMarkerIndex !== -1 && endMarkerIndex > startMarkerIndex) {
|
|
256
|
-
// Both markers exist, replace content between them
|
|
257
|
-
const beforeSection = existingContent.substring(0, startMarkerIndex);
|
|
258
|
-
const afterSection = existingContent.substring(endMarkerIndex + config.GITIGNORE_END_MARKER.length);
|
|
259
|
-
|
|
260
|
-
// Remove trailing newlines from before section and leading newlines from after section
|
|
261
|
-
const beforeTrimmed = beforeSection.replace(/\n+$/, '');
|
|
262
|
-
const afterTrimmed = afterSection.replace(/^\n+/, '');
|
|
263
|
-
|
|
264
|
-
const parts = [beforeTrimmed];
|
|
265
|
-
if (beforeTrimmed) parts.push(''); // Add separator if there's content before
|
|
266
|
-
parts.push(
|
|
267
|
-
config.GITIGNORE_START_MARKER,
|
|
268
|
-
rulesContent,
|
|
269
|
-
config.GITIGNORE_END_MARKER
|
|
270
|
-
);
|
|
271
|
-
if (afterTrimmed) {
|
|
272
|
-
parts.push(''); // Add separator if there's content after
|
|
273
|
-
parts.push(afterTrimmed);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
newContent = parts.join('\n');
|
|
277
|
-
} else if (startMarkerIndex !== -1 || endMarkerIndex !== -1) {
|
|
278
|
-
// Only one marker exists, append to end
|
|
279
|
-
const trimmed = existingContent.replace(/\n+$/, '');
|
|
280
|
-
newContent = [
|
|
281
|
-
trimmed,
|
|
282
|
-
'',
|
|
283
|
-
config.GITIGNORE_START_MARKER,
|
|
284
|
-
rulesContent,
|
|
285
|
-
config.GITIGNORE_END_MARKER
|
|
286
|
-
].join('\n');
|
|
287
|
-
} else {
|
|
288
|
-
// No markers exist, append to end
|
|
289
|
-
if (existingContent.trim() === '') {
|
|
290
|
-
// File is empty or only whitespace
|
|
291
|
-
newContent = [
|
|
292
|
-
config.GITIGNORE_START_MARKER,
|
|
293
|
-
rulesContent,
|
|
294
|
-
config.GITIGNORE_END_MARKER
|
|
295
|
-
].join('\n');
|
|
296
|
-
} else {
|
|
297
|
-
// File has content, append with newline
|
|
298
|
-
const trimmed = existingContent.replace(/\n+$/, '');
|
|
299
|
-
newContent = [
|
|
300
|
-
trimmed,
|
|
301
|
-
'',
|
|
302
|
-
config.GITIGNORE_START_MARKER,
|
|
303
|
-
rulesContent,
|
|
304
|
-
config.GITIGNORE_END_MARKER
|
|
305
|
-
].join('\n');
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// Write the updated content
|
|
310
|
-
if (options.dryRun) {
|
|
311
|
-
console.log(config.dryRunText(config.GITIGNORE_NAME, gitignorePath));
|
|
312
|
-
console.log();
|
|
313
|
-
} else {
|
|
314
|
-
try {
|
|
315
|
-
await fs.writeFile(gitignorePath, newContent, 'utf-8');
|
|
316
|
-
console.log(`Updated .gitignore: ${gitignorePath}`);
|
|
317
|
-
} catch (error) {
|
|
318
|
-
console.warn(`Warning: Could not write .gitignore file: ${error.message}`);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* Generate the README.md files for each category
|
|
325
|
-
* @param {Lockfile} lockfile - The lockfile object
|
|
326
|
-
* @param {string} workingDir - The working directory
|
|
327
|
-
* @param {Options | InitOptions} options - The options object
|
|
328
|
-
*/
|
|
329
|
-
export async function generateReadmeFiles(lockfile, workingDir, options = {}) {
|
|
330
|
-
// Collect unique project IDs and author IDs from version data
|
|
331
|
-
const projectIds = new Set();
|
|
332
|
-
const authorIds = new Set();
|
|
333
|
-
|
|
334
|
-
for (const [category, entries] of Object.entries(lockfile.dependencies)) {
|
|
335
|
-
for (const entry of entries) {
|
|
336
|
-
if (entry.version && entry.version.project_id) {
|
|
337
|
-
projectIds.add(entry.version.project_id);
|
|
338
|
-
}
|
|
339
|
-
if (entry.version && entry.version.author_id) {
|
|
340
|
-
authorIds.add(entry.version.author_id);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// Fetch projects and users in parallel
|
|
346
|
-
console.log(`Fetching data for ${projectIds.size} project(s) and ${authorIds.size} user(s)...`);
|
|
347
|
-
|
|
348
|
-
const [projects, users] = await Promise.all([
|
|
349
|
-
getProjects(Array.from(projectIds)),
|
|
350
|
-
getUsers(Array.from(authorIds)),
|
|
351
|
-
]);
|
|
352
|
-
|
|
353
|
-
// Map projects and users to their IDs
|
|
354
|
-
const projectsMap = {};
|
|
355
|
-
for (const project of projects) {
|
|
356
|
-
projectsMap[project.id] = project;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
const usersMap = {};
|
|
360
|
-
for (const user of users) {
|
|
361
|
-
usersMap[user.id] = user;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
// Generate README for each category
|
|
365
|
-
for (const [category, entries] of Object.entries(lockfile.dependencies)) {
|
|
366
|
-
if (entries.length === 0) {
|
|
367
|
-
continue;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
const readmeContent = generateCategoryReadme(category, entries, projectsMap, usersMap);
|
|
371
|
-
const categoryDir = getScanDirectories(workingDir).find(d => d.name === category);
|
|
372
|
-
|
|
373
|
-
if (categoryDir) {
|
|
374
|
-
const readmePath = path.join(categoryDir.path, config.README_NAME);
|
|
375
|
-
|
|
376
|
-
if (options.dryRun) {
|
|
377
|
-
console.log(config.dryRunText(config.README_NAME, readmePath));
|
|
378
|
-
} else {
|
|
379
|
-
try {
|
|
380
|
-
await fs.writeFile(readmePath, readmeContent, 'utf-8');
|
|
381
|
-
console.log(`Generated README: ${readmePath}`);
|
|
382
|
-
} catch (error) {
|
|
383
|
-
console.warn(`Warning: Could not write README to ${readmePath}: ${error.message}`);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
console.log('README generation complete.');
|
|
69
|
+
await fs.writeFile(outputPath, content, "utf-8");
|
|
70
|
+
logm.generated(config.MODPACK_LOCKFILE_NAME, outputPath);
|
|
390
71
|
}
|
|
391
72
|
|
|
392
73
|
/**
|
|
@@ -396,46 +77,61 @@ export async function generateReadmeFiles(lockfile, workingDir, options = {}) {
|
|
|
396
77
|
* @returns {Lockfile} The lockfile object
|
|
397
78
|
*/
|
|
398
79
|
export async function generateLockfile(workingDir, options = {}) {
|
|
399
|
-
|
|
80
|
+
logm.quietFromOptions(options);
|
|
81
|
+
|
|
82
|
+
logm.header("Scanning Directories");
|
|
400
83
|
|
|
401
84
|
// Scan all directories
|
|
402
85
|
const allFileEntries = [];
|
|
403
86
|
for (const dirInfo of getScanDirectories(workingDir)) {
|
|
404
|
-
|
|
87
|
+
logm.info(styleText(["cyan"], `${dirInfo.name}/`));
|
|
405
88
|
const fileEntries = await scanDirectory(dirInfo, workingDir);
|
|
406
|
-
|
|
89
|
+
logm.info(
|
|
90
|
+
styleText(["dim"], ` └─ Found`),
|
|
91
|
+
styleText(["yellow"], `${fileEntries.length}`),
|
|
92
|
+
styleText(["dim"], `file${fileEntries.length !== 1 ? "s" : ""}`),
|
|
93
|
+
);
|
|
407
94
|
allFileEntries.push(...fileEntries);
|
|
408
95
|
}
|
|
409
96
|
|
|
410
97
|
// Sort file entries
|
|
411
98
|
allFileEntries.sort((a, b) => {
|
|
412
99
|
if (a.category !== b.category) {
|
|
413
|
-
return a.category.localeCompare(b.category,
|
|
100
|
+
return a.category.localeCompare(b.category, "en", {sensitivity: "base"});
|
|
414
101
|
}
|
|
415
|
-
return a.path.localeCompare(b.path,
|
|
102
|
+
return a.path.localeCompare(b.path, "en", {numeric: true, sensitivity: "base"});
|
|
416
103
|
});
|
|
417
104
|
|
|
418
105
|
if (allFileEntries.length === 0) {
|
|
419
|
-
|
|
106
|
+
logm.header("GENERATING LOCKFILE");
|
|
107
|
+
logm.warn("No files found. Creating empty lockfile.");
|
|
108
|
+
const emptyLockfile = createEmptyLockfile();
|
|
420
109
|
const outputPath = path.join(workingDir, config.MODPACK_LOCKFILE_NAME);
|
|
421
110
|
if (options.dryRun) {
|
|
422
|
-
|
|
111
|
+
logm.debug(config.dryRunText(config.MODPACK_LOCKFILE_NAME, outputPath));
|
|
423
112
|
} else {
|
|
424
|
-
await writeLockfile(
|
|
113
|
+
await writeLockfile(emptyLockfile, outputPath);
|
|
425
114
|
}
|
|
426
|
-
return;
|
|
115
|
+
return emptyLockfile;
|
|
427
116
|
}
|
|
428
117
|
|
|
429
|
-
|
|
430
|
-
|
|
118
|
+
logm.info(styleText(["dim"], "Total:"), allFileEntries.length);
|
|
119
|
+
logm.header("Querying Modrinth API");
|
|
431
120
|
|
|
432
121
|
// Extract all hashes
|
|
433
|
-
const hashes = allFileEntries.map(info => info.hash);
|
|
122
|
+
const hashes = allFileEntries.map((info) => info.hash);
|
|
434
123
|
|
|
435
124
|
// Query Modrinth API
|
|
436
125
|
const versionData = await getVersionsFromHashes(hashes);
|
|
437
126
|
|
|
438
|
-
|
|
127
|
+
logm.info(styleText(["dim"], "Found version information for:"));
|
|
128
|
+
logm.info(
|
|
129
|
+
styleText(["dim"], " └─"),
|
|
130
|
+
styleText(["green"], `${Object.keys(versionData).length}`),
|
|
131
|
+
styleText(["dim"], "out of"),
|
|
132
|
+
styleText(["yellow"], `${hashes.length}`),
|
|
133
|
+
styleText(["dim"], "files"),
|
|
134
|
+
);
|
|
439
135
|
|
|
440
136
|
// Create lockfile
|
|
441
137
|
const lockfile = createLockfile(allFileEntries, versionData);
|
|
@@ -443,29 +139,40 @@ export async function generateLockfile(workingDir, options = {}) {
|
|
|
443
139
|
// Write lockfile
|
|
444
140
|
const outputPath = path.join(workingDir, config.MODPACK_LOCKFILE_NAME);
|
|
445
141
|
if (options.dryRun) {
|
|
446
|
-
|
|
142
|
+
logm.debug(config.dryRunText(config.MODPACK_LOCKFILE_NAME, outputPath));
|
|
447
143
|
} else {
|
|
448
144
|
await writeLockfile(lockfile, outputPath);
|
|
449
145
|
}
|
|
450
146
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
for (const [category, entries] of Object.entries(lockfile.dependencies)) {
|
|
454
|
-
const withVersion = entries.filter(e => e.version !== null).length;
|
|
455
|
-
const withoutVersion = entries.length - withVersion;
|
|
456
|
-
console.log(`${category}: ${entries.length} file(s) (${withVersion} found on Modrinth, ${withoutVersion} unknown)`);
|
|
457
|
-
}
|
|
147
|
+
return lockfile;
|
|
148
|
+
}
|
|
458
149
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
150
|
+
/**
|
|
151
|
+
* Print a summary of the lockfile contents
|
|
152
|
+
* @param {Lockfile} lockfile - The lockfile object
|
|
153
|
+
*/
|
|
154
|
+
export function printLockfileSummary(lockfile) {
|
|
155
|
+
logm.header("Lockfile Summary");
|
|
463
156
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
await generateReadmeFiles(lockfile, workingDir, options);
|
|
157
|
+
if (lockfile.total === 0) {
|
|
158
|
+
logm.info(styleText(["dim"], "No files found. Empty lockfile created."));
|
|
159
|
+
return;
|
|
468
160
|
}
|
|
469
161
|
|
|
470
|
-
|
|
162
|
+
for (const [category, entries] of Object.entries(lockfile.dependencies)) {
|
|
163
|
+
const withVersion = entries.filter((e) => e.version !== null).length;
|
|
164
|
+
const withoutVersion = entries.length - withVersion;
|
|
165
|
+
logm.info(
|
|
166
|
+
styleText(["bold"], `${category}:`),
|
|
167
|
+
entries.length,
|
|
168
|
+
styleText(["dim"], `file${entries.length !== 1 ? "s" : ""}`),
|
|
169
|
+
);
|
|
170
|
+
logm.info(
|
|
171
|
+
styleText(["dim"], " └─"),
|
|
172
|
+
styleText(["green"], String(withVersion)),
|
|
173
|
+
styleText(["dim"], "found,"),
|
|
174
|
+
styleText(["yellow"], String(withoutVersion)),
|
|
175
|
+
styleText(["dim"], "unknown"),
|
|
176
|
+
);
|
|
177
|
+
}
|
|
471
178
|
}
|