modpack-lock 0.4.0 → 0.5.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/README.md +82 -59
- package/package.json +1 -1
- package/src/cli.js +56 -27
- package/src/config/api.js +18 -0
- package/src/config/constants.js +18 -0
- package/src/config/defaults.js +58 -0
- package/src/config/files.js +9 -0
- package/src/config/index.js +1 -0
- package/src/config/options.js +15 -1
- package/src/config/strings.js +85 -0
- package/src/config/types.js +30 -0
- package/src/directory_scanning.js +4 -5
- package/src/generate_json.js +3 -3
- package/src/generate_license.js +50 -0
- package/src/generate_lockfile.js +100 -19
- package/src/github_interactions.js +73 -0
- package/src/modpack-lock.js +5 -3
- package/src/modpack_info.js +191 -111
- package/src/modrinth_interactions.js +57 -1
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import * as defaults from './defaults.js';
|
|
2
|
+
import pkg from '../../package.json' with { type: 'json' };
|
|
3
|
+
|
|
4
|
+
export const infoFields = {
|
|
5
|
+
name: {
|
|
6
|
+
prompt: 'modpack name',
|
|
7
|
+
option: 'Modpack name; defaults to the directory name'
|
|
8
|
+
},
|
|
9
|
+
version: {
|
|
10
|
+
prompt: 'modpack version',
|
|
11
|
+
option: `Modpack version; defaults to ${defaults.DEFAULT_MODPACK_VERSION}`
|
|
12
|
+
},
|
|
13
|
+
id: {
|
|
14
|
+
prompt: 'modpack slug/ID',
|
|
15
|
+
option: 'Modpack slug/ID; defaults to the directory name slugified'
|
|
16
|
+
},
|
|
17
|
+
description: {
|
|
18
|
+
prompt: 'modpack description',
|
|
19
|
+
option: 'Modpack description'
|
|
20
|
+
},
|
|
21
|
+
author: {
|
|
22
|
+
prompt: 'modpack author',
|
|
23
|
+
option: 'Modpack author; required'
|
|
24
|
+
},
|
|
25
|
+
projectUrl: {
|
|
26
|
+
prompt: 'modpack URL',
|
|
27
|
+
option: 'Modpack URL; defaults to a guessed Modrinth project URL'
|
|
28
|
+
},
|
|
29
|
+
sourceUrl: {
|
|
30
|
+
prompt: 'modpack source code URL',
|
|
31
|
+
option: 'Modpack source code URL; defaults to a guessed GitHub repository URL'
|
|
32
|
+
},
|
|
33
|
+
license: {
|
|
34
|
+
prompt: 'modpack license',
|
|
35
|
+
option: `Modpack license, popular licenses fetched from GitHub; defaults to ${defaults.DEFAULT_MODPACK_LICENSE} in interactive mode`
|
|
36
|
+
},
|
|
37
|
+
modloader: {
|
|
38
|
+
prompt: 'modpack modloader',
|
|
39
|
+
option: 'Modpack modloader, list of loaders fetched from Modrinth; required'
|
|
40
|
+
},
|
|
41
|
+
targetModloaderVersion: {
|
|
42
|
+
prompt: 'target modloader version',
|
|
43
|
+
option: 'Target modloader version'
|
|
44
|
+
},
|
|
45
|
+
targetMinecraftVersion: {
|
|
46
|
+
prompt: 'target Minecraft version',
|
|
47
|
+
option: 'Target Minecraft version, list of versions fetched from Modrinth; required'
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const fileFields = {
|
|
52
|
+
addLicense: {
|
|
53
|
+
prompt: 'Add the LICENSE file',
|
|
54
|
+
option: 'Add the LICENSE file to the modpack'
|
|
55
|
+
},
|
|
56
|
+
addGitignore: {
|
|
57
|
+
prompt: 'Update the .gitignore file',
|
|
58
|
+
option: 'Update the .gitignore file to ignore content hosted on Modrinth'
|
|
59
|
+
},
|
|
60
|
+
addReadme: {
|
|
61
|
+
prompt: 'Generate README.md files',
|
|
62
|
+
option: 'Generate README.md files for each category'
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const headings = {
|
|
67
|
+
options: "Options:",
|
|
68
|
+
generation: "GENERATION",
|
|
69
|
+
logging: "LOGGING",
|
|
70
|
+
packInfo: "MODPACK INFORMATION",
|
|
71
|
+
information: "INFORMATION"
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const dryRunText = (filename, location) => {
|
|
75
|
+
return `[DRY RUN] Would write ${filename} to: ${location}`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** All-Rights-Reserved license text */
|
|
79
|
+
export const ARR_LICENSE_TEXT =
|
|
80
|
+
"Copyright (c) [year] [fullname]\n" +
|
|
81
|
+
"\n" +
|
|
82
|
+
"All rights reserved.\n";
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
export {pkg};
|
package/src/config/types.js
CHANGED
|
@@ -42,4 +42,34 @@
|
|
|
42
42
|
* @property {boolean} readme - Whether to generate README.md files
|
|
43
43
|
*/
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* @typedef {Object} InitOptions
|
|
47
|
+
* Contains options for the initialization of the modpack files.
|
|
48
|
+
* @property {string} folder - The folder to generate the modpack files in
|
|
49
|
+
* @property {boolean} noninteractive - Whether to run the interactive mode
|
|
50
|
+
* @property {boolean} addLicense - Whether to add the license file to the modpack
|
|
51
|
+
* @property {boolean} gitignore - Whether to generate .gitignore rules
|
|
52
|
+
* @property {boolean} readme - Whether to generate README.md files
|
|
53
|
+
* @property {string} name - The name of the modpack
|
|
54
|
+
* @property {string} version - The version of the modpack
|
|
55
|
+
* @property {string} id - The slug/ID of the modpack
|
|
56
|
+
* @property {string} description - The description of the modpack
|
|
57
|
+
* @property {string} author - The author of the modpack
|
|
58
|
+
* @property {string} projectUrl - The modpack's project URL
|
|
59
|
+
* @property {string} sourceUrl - The modpack's source code URL
|
|
60
|
+
* @property {string} license - The modpack's license
|
|
61
|
+
* @property {string} modloader - The modpack's modloader
|
|
62
|
+
* @property {string} targetModloaderVersion - The target modloader version
|
|
63
|
+
* @property {string} targetMinecraftVersion - The target Minecraft version
|
|
64
|
+
* @property {boolean} _init - Internal boolean added to indicate options come from the `init` command.
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @typedef {Object} RunOptions
|
|
69
|
+
* Contains options for the running scripts defined in modpack.json.
|
|
70
|
+
* @property {string} folder - The folder to look for modpack.json in
|
|
71
|
+
* @property {boolean} debug - Whether to print debug information about the executed script
|
|
72
|
+
* @property {boolean} _run - Internal boolean added to indicate options come from the `run` command.
|
|
73
|
+
*/
|
|
74
|
+
|
|
45
75
|
export {};
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
2
|
import crypto from 'crypto';
|
|
3
3
|
import path from 'path';
|
|
4
|
-
import * as
|
|
5
|
-
import * as constants from './config/constants.js';
|
|
4
|
+
import * as config from './config/index.js';
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* @typedef {import('./config/types.js').ModpackInfo} ModpackInfo
|
|
@@ -16,7 +15,7 @@ import * as constants from './config/constants.js';
|
|
|
16
15
|
*/
|
|
17
16
|
export function getScanDirectories(directoryPath) {
|
|
18
17
|
const scanDirectories = [];
|
|
19
|
-
for (const category of
|
|
18
|
+
for (const category of config.DEPENDENCY_CATEGORIES) {
|
|
20
19
|
scanDirectories.push({ name: category, path: path.join(directoryPath, category) });
|
|
21
20
|
}
|
|
22
21
|
return scanDirectories;
|
|
@@ -105,7 +104,7 @@ async function getJsonFile(directoryPath, filename) {
|
|
|
105
104
|
* @returns {Promise<ModpackInfo|null>} The modpack info JSON object if the file exists, otherwise null
|
|
106
105
|
*/
|
|
107
106
|
export async function getModpackInfo(directoryPath) {
|
|
108
|
-
return getJsonFile(directoryPath,
|
|
107
|
+
return getJsonFile(directoryPath, config.MODPACK_JSON_NAME);
|
|
109
108
|
}
|
|
110
109
|
|
|
111
110
|
/**
|
|
@@ -114,5 +113,5 @@ export async function getModpackInfo(directoryPath) {
|
|
|
114
113
|
* @returns {Lockfile|null} The JSON object if the file exists, otherwise null
|
|
115
114
|
*/
|
|
116
115
|
export async function getLockfile(directoryPath) {
|
|
117
|
-
return getJsonFile(directoryPath,
|
|
116
|
+
return getJsonFile(directoryPath, config.MODPACK_LOCKFILE_NAME);
|
|
118
117
|
}
|
package/src/generate_json.js
CHANGED
|
@@ -6,6 +6,7 @@ import * as config from './config/index.js';
|
|
|
6
6
|
/**
|
|
7
7
|
* @typedef {import('./config/types.js').ModpackInfo} ModpackInfo
|
|
8
8
|
* @typedef {import('./config/types.js').Options} Options
|
|
9
|
+
* @typedef {import('./config/types.js').InitOptions} InitOptions
|
|
9
10
|
* @typedef {import('./config/types.js').Lockfile} Lockfile
|
|
10
11
|
*/
|
|
11
12
|
|
|
@@ -33,7 +34,7 @@ async function writeJson(jsonObject, outputPath) {
|
|
|
33
34
|
* @param {ModpackInfo} modpackInfo - The modpack information
|
|
34
35
|
* @param {Lockfile} lockfile - The lockfile
|
|
35
36
|
* @param {string} outputDir - The path to write the JSON object to
|
|
36
|
-
* @param {Options} options - The options object
|
|
37
|
+
* @param {Options | InitOptions} options - The options object
|
|
37
38
|
* @returns {Promise<Lockfile>} The JSON file's object
|
|
38
39
|
*/
|
|
39
40
|
export default async function generateJson(modpackInfo, lockfile, outputDir, options = {}) {
|
|
@@ -84,7 +85,6 @@ export default async function generateJson(modpackInfo, lockfile, outputDir, opt
|
|
|
84
85
|
packDependencies[category].push(projectSlug);
|
|
85
86
|
}
|
|
86
87
|
}
|
|
87
|
-
//packDependencies[category].push(...packDependencies[category].map(item => item.path));
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
|
|
@@ -93,7 +93,7 @@ export default async function generateJson(modpackInfo, lockfile, outputDir, opt
|
|
|
93
93
|
|
|
94
94
|
// Write modpack JSON object to disk
|
|
95
95
|
if (options.dryRun) {
|
|
96
|
-
console.log(
|
|
96
|
+
console.log(config.dryRunText(config.MODPACK_JSON_NAME, path.join(outputDir, config.MODPACK_JSON_NAME)));
|
|
97
97
|
} else {
|
|
98
98
|
await writeJson(jsonObject, outputDir);
|
|
99
99
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { getLicenseText } from './github_interactions.js';
|
|
4
|
+
import * as config from './config/index.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {import('./config/types.js').ModpackInfo} ModpackInfo
|
|
8
|
+
* @typedef {import('./config/types.js').Options} Options
|
|
9
|
+
* @typedef {import('./config/types.js').InitOptions} InitOptions
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
async function writeLicense(licenseText, outputPath) {
|
|
13
|
+
await fs.writeFile(path.join(outputPath, config.MODPACK_LICENSE_NAME), licenseText, 'utf-8');
|
|
14
|
+
console.log(`${config.MODPACK_LICENSE_NAME} written to: ${path.join(outputPath, config.MODPACK_LICENSE_NAME)}`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Write a license to a file
|
|
19
|
+
* @param {ModpackInfo} modpackInfo - The modpack information
|
|
20
|
+
* @param {string} outputPath - The path to write the license to
|
|
21
|
+
* @param {InitOptions} options - The initialization options object
|
|
22
|
+
* @param {string} licenseTextOverride - The license text to override the default license text with
|
|
23
|
+
* @returns {Promise<string> | null} The license text or null if the license text could not be generated
|
|
24
|
+
*/
|
|
25
|
+
export default async function generateLicense(modpackInfo, outputPath, options = {}, licenseTextOverride = null) {
|
|
26
|
+
try {
|
|
27
|
+
const spdxId = modpackInfo.license;
|
|
28
|
+
console.log(`Generating license for: ${spdxId}`);
|
|
29
|
+
|
|
30
|
+
let licenseText = licenseTextOverride || await getLicenseText(spdxId);
|
|
31
|
+
licenseText = licenseText.replace('[year]', new Date().getFullYear());
|
|
32
|
+
licenseText = licenseText.replace('{{year}}', new Date().getFullYear());
|
|
33
|
+
licenseText = licenseText.replace('[fullname]', modpackInfo.author);
|
|
34
|
+
licenseText = licenseText.replace('{{fullname}}', modpackInfo.author);
|
|
35
|
+
licenseText = licenseText.replace('[organization]', modpackInfo.author);
|
|
36
|
+
licenseText = licenseText.replace('{{organization}}', modpackInfo.author);
|
|
37
|
+
licenseText = licenseText.replace('[project]', modpackInfo.name);
|
|
38
|
+
licenseText = licenseText.replace('{{project}}', modpackInfo.name);
|
|
39
|
+
|
|
40
|
+
if (options.dryRun) {
|
|
41
|
+
console.log(config.dryRunText(config.MODPACK_LICENSE_NAME, path.join(outputPath, config.MODPACK_LICENSE_NAME)));
|
|
42
|
+
} else {
|
|
43
|
+
await writeLicense(licenseText, outputPath);
|
|
44
|
+
}
|
|
45
|
+
return licenseText;
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.warn(`Warning: unable to generate license for: ${modpackInfo.license}`);
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/generate_lockfile.js
CHANGED
|
@@ -6,6 +6,7 @@ import * as config from './config/index.js';
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* @typedef {import('./config/types.js').Options} Options
|
|
9
|
+
* @typedef {import('./config/types.js').InitOptions} InitOptions
|
|
9
10
|
* @typedef {import('./config/types.js').Lockfile} Lockfile
|
|
10
11
|
*/
|
|
11
12
|
|
|
@@ -137,11 +138,12 @@ function generateCategoryReadme(category, entries, projectsMap, usersMap) {
|
|
|
137
138
|
}
|
|
138
139
|
|
|
139
140
|
/**
|
|
140
|
-
* Generate .gitignore rules for files not hosted on Modrinth
|
|
141
|
+
* Generate .gitignore rules for files not hosted on Modrinth and write them to .gitignore file
|
|
141
142
|
* @param {Lockfile} lockfile - The lockfile object
|
|
142
|
-
* @
|
|
143
|
+
* @param {string} workingDir - The working directory
|
|
144
|
+
* @param {Options | InitOptions} options - The options object
|
|
143
145
|
*/
|
|
144
|
-
export function generateGitignoreRules(lockfile) {
|
|
146
|
+
export async function generateGitignoreRules(lockfile, workingDir, options = {}) {
|
|
145
147
|
const rules = [];
|
|
146
148
|
const exceptions = [];
|
|
147
149
|
|
|
@@ -149,7 +151,7 @@ export function generateGitignoreRules(lockfile) {
|
|
|
149
151
|
for (const category of config.DEPENDENCY_CATEGORIES) {
|
|
150
152
|
rules.push(`${category}/*.${category === "mods" ? "jar" : "zip"}`);
|
|
151
153
|
}
|
|
152
|
-
rules.push(
|
|
154
|
+
rules.push(`*/**/*.disabled`);
|
|
153
155
|
|
|
154
156
|
// Find files not hosted on Modrinth
|
|
155
157
|
for (const [category, entries] of Object.entries(lockfile.dependencies)) {
|
|
@@ -162,19 +164,104 @@ export function generateGitignoreRules(lockfile) {
|
|
|
162
164
|
|
|
163
165
|
// Add exceptions if any
|
|
164
166
|
if (exceptions.length > 0) {
|
|
167
|
+
rules.push('\n## Exceptions');
|
|
165
168
|
rules.push(...exceptions);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const rulesContent = rules.join('\n');
|
|
172
|
+
const gitignorePath = path.join(workingDir, config.GITIGNORE_NAME);
|
|
173
|
+
|
|
174
|
+
// Read existing .gitignore file if it exists
|
|
175
|
+
let existingContent = '';
|
|
176
|
+
try {
|
|
177
|
+
existingContent = await fs.readFile(gitignorePath, 'utf-8');
|
|
178
|
+
} catch (error) {
|
|
179
|
+
// File doesn't exist, that's okay - we'll create it
|
|
180
|
+
if (error.code !== 'ENOENT') {
|
|
181
|
+
console.warn(`Warning: Could not read .gitignore file: ${error.message}`);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Find markers in existing content
|
|
187
|
+
const startMarkerIndex = existingContent.indexOf(config.GITIGNORE_START_MARKER);
|
|
188
|
+
const endMarkerIndex = existingContent.indexOf(config.GITIGNORE_END_MARKER);
|
|
189
|
+
|
|
190
|
+
let newContent = '';
|
|
191
|
+
|
|
192
|
+
if (startMarkerIndex !== -1 && endMarkerIndex !== -1 && endMarkerIndex > startMarkerIndex) {
|
|
193
|
+
// Both markers exist, replace content between them
|
|
194
|
+
const beforeSection = existingContent.substring(0, startMarkerIndex);
|
|
195
|
+
const afterSection = existingContent.substring(endMarkerIndex + config.GITIGNORE_END_MARKER.length);
|
|
196
|
+
|
|
197
|
+
// Remove trailing newlines from before section and leading newlines from after section
|
|
198
|
+
const beforeTrimmed = beforeSection.replace(/\n+$/, '');
|
|
199
|
+
const afterTrimmed = afterSection.replace(/^\n+/, '');
|
|
200
|
+
|
|
201
|
+
const parts = [beforeTrimmed];
|
|
202
|
+
if (beforeTrimmed) parts.push(''); // Add separator if there's content before
|
|
203
|
+
parts.push(
|
|
204
|
+
config.GITIGNORE_START_MARKER,
|
|
205
|
+
rulesContent,
|
|
206
|
+
config.GITIGNORE_END_MARKER
|
|
207
|
+
);
|
|
208
|
+
if (afterTrimmed) {
|
|
209
|
+
parts.push(''); // Add separator if there's content after
|
|
210
|
+
parts.push(afterTrimmed);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
newContent = parts.join('\n');
|
|
214
|
+
} else if (startMarkerIndex !== -1 || endMarkerIndex !== -1) {
|
|
215
|
+
// Only one marker exists, append to end
|
|
216
|
+
const trimmed = existingContent.replace(/\n+$/, '');
|
|
217
|
+
newContent = [
|
|
218
|
+
trimmed,
|
|
219
|
+
'',
|
|
220
|
+
config.GITIGNORE_START_MARKER,
|
|
221
|
+
rulesContent,
|
|
222
|
+
config.GITIGNORE_END_MARKER
|
|
223
|
+
].join('\n');
|
|
166
224
|
} else {
|
|
167
|
-
|
|
225
|
+
// No markers exist, append to end
|
|
226
|
+
if (existingContent.trim() === '') {
|
|
227
|
+
// File is empty or only whitespace
|
|
228
|
+
newContent = [
|
|
229
|
+
config.GITIGNORE_START_MARKER,
|
|
230
|
+
rulesContent,
|
|
231
|
+
config.GITIGNORE_END_MARKER
|
|
232
|
+
].join('\n');
|
|
233
|
+
} else {
|
|
234
|
+
// File has content, append with newline
|
|
235
|
+
const trimmed = existingContent.replace(/\n+$/, '');
|
|
236
|
+
newContent = [
|
|
237
|
+
trimmed,
|
|
238
|
+
'',
|
|
239
|
+
config.GITIGNORE_START_MARKER,
|
|
240
|
+
rulesContent,
|
|
241
|
+
config.GITIGNORE_END_MARKER
|
|
242
|
+
].join('\n');
|
|
243
|
+
}
|
|
168
244
|
}
|
|
169
245
|
|
|
170
|
-
|
|
246
|
+
// Write the updated content
|
|
247
|
+
if (options.dryRun) {
|
|
248
|
+
console.log(config.dryRunText(config.GITIGNORE_NAME, gitignorePath));
|
|
249
|
+
console.log();
|
|
250
|
+
} else {
|
|
251
|
+
try {
|
|
252
|
+
await fs.writeFile(gitignorePath, newContent, 'utf-8');
|
|
253
|
+
console.log(`Updated .gitignore: ${gitignorePath}`);
|
|
254
|
+
} catch (error) {
|
|
255
|
+
console.warn(`Warning: Could not write .gitignore file: ${error.message}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
171
258
|
}
|
|
172
259
|
|
|
173
260
|
/**
|
|
174
261
|
* Generate the README.md files for each category
|
|
175
262
|
* @param {Lockfile} lockfile - The lockfile object
|
|
176
263
|
* @param {string} workingDir - The working directory
|
|
177
|
-
* @param {Options} options - The options object
|
|
264
|
+
* @param {Options | InitOptions} options - The options object
|
|
178
265
|
*/
|
|
179
266
|
export async function generateReadmeFiles(lockfile, workingDir, options = {}) {
|
|
180
267
|
// Collect unique project IDs and author IDs from version data
|
|
@@ -221,10 +308,10 @@ export async function generateReadmeFiles(lockfile, workingDir, options = {}) {
|
|
|
221
308
|
const categoryDir = getScanDirectories(workingDir).find(d => d.name === category);
|
|
222
309
|
|
|
223
310
|
if (categoryDir) {
|
|
224
|
-
const readmePath = path.join(categoryDir.path,
|
|
311
|
+
const readmePath = path.join(categoryDir.path, config.README_NAME);
|
|
225
312
|
|
|
226
313
|
if (options.dryRun) {
|
|
227
|
-
console.log(
|
|
314
|
+
console.log(config.dryRunText(config.README_NAME, readmePath));
|
|
228
315
|
} else {
|
|
229
316
|
try {
|
|
230
317
|
await fs.writeFile(readmePath, readmeContent, 'utf-8');
|
|
@@ -246,10 +333,6 @@ export async function generateReadmeFiles(lockfile, workingDir, options = {}) {
|
|
|
246
333
|
* @returns {Lockfile} The lockfile object
|
|
247
334
|
*/
|
|
248
335
|
export async function generateLockfile(workingDir, options = {}) {
|
|
249
|
-
if (options.dryRun) {
|
|
250
|
-
console.log('[DRY RUN] Preview mode - no files will be written');
|
|
251
|
-
}
|
|
252
|
-
|
|
253
336
|
console.log('Scanning directories for modpack files...');
|
|
254
337
|
|
|
255
338
|
// Scan all directories
|
|
@@ -273,7 +356,7 @@ export async function generateLockfile(workingDir, options = {}) {
|
|
|
273
356
|
console.log('No files found. Creating empty lockfile.');
|
|
274
357
|
const outputPath = path.join(workingDir, config.MODPACK_LOCKFILE_NAME);
|
|
275
358
|
if (options.dryRun) {
|
|
276
|
-
console.log(
|
|
359
|
+
console.log(config.dryRunText(config.MODPACK_LOCKFILE_NAME, outputPath));
|
|
277
360
|
} else {
|
|
278
361
|
await writeLockfile(createEmptyLockfile(), outputPath);
|
|
279
362
|
}
|
|
@@ -297,7 +380,7 @@ export async function generateLockfile(workingDir, options = {}) {
|
|
|
297
380
|
// Write lockfile
|
|
298
381
|
const outputPath = path.join(workingDir, config.MODPACK_LOCKFILE_NAME);
|
|
299
382
|
if (options.dryRun) {
|
|
300
|
-
console.log(
|
|
383
|
+
console.log(config.dryRunText(config.MODPACK_LOCKFILE_NAME, outputPath));
|
|
301
384
|
} else {
|
|
302
385
|
await writeLockfile(lockfile, outputPath);
|
|
303
386
|
}
|
|
@@ -312,15 +395,13 @@ export async function generateLockfile(workingDir, options = {}) {
|
|
|
312
395
|
|
|
313
396
|
// Generate .gitignore rules
|
|
314
397
|
if (options.gitignore) {
|
|
315
|
-
|
|
316
|
-
console.log(generateGitignoreRules(lockfile));
|
|
317
|
-
console.log();
|
|
398
|
+
await generateGitignoreRules(lockfile, workingDir, options);
|
|
318
399
|
}
|
|
319
400
|
|
|
320
401
|
// Generate README files
|
|
321
402
|
if (options.readme) {
|
|
322
403
|
console.log('\nGenerating README files...');
|
|
323
|
-
await generateReadmeFiles(lockfile, workingDir, options
|
|
404
|
+
await generateReadmeFiles(lockfile, workingDir, options);
|
|
324
405
|
}
|
|
325
406
|
|
|
326
407
|
return lockfile;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import * as config from './config/index.js';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Fetch a list of the most popular licenses from GitHub
|
|
6
|
+
* @param {boolean} featured - If the fetch should be limited to featured licenses
|
|
7
|
+
* @returns {Promise<Array<Object>>} The list of licenses for use in a prompt
|
|
8
|
+
*/
|
|
9
|
+
export async function getLicenseList(featured = false) {
|
|
10
|
+
try {
|
|
11
|
+
const url = featured ? config.GITHUB_FEATURED_LICENSES_ENDPOINT : config.GITHUB_LICENSES_ENDPOINT;
|
|
12
|
+
const response = await fetch(url);
|
|
13
|
+
if (!response.ok) {
|
|
14
|
+
const errorText = await response.text();
|
|
15
|
+
throw new Error(`GitHub API error (${response.status}): ${errorText}`);
|
|
16
|
+
}
|
|
17
|
+
let licenseList = await response.json();
|
|
18
|
+
|
|
19
|
+
let licenseSpdxIds = licenseList.map(license => ({ title: license.spdx_id, value: license.key }));
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
if (!featured) {
|
|
23
|
+
// get featured licenses and place them at the beginning of the list, removing them from the original list
|
|
24
|
+
licenseSpdxIds.unshift(config.ALL_RIGHTS_RESERVED_LICENSE);
|
|
25
|
+
licenseSpdxIds.push(config.OTHER_OPTION);
|
|
26
|
+
const featuredLicenseList = await getLicenseList(true);
|
|
27
|
+
for (const license of featuredLicenseList) {
|
|
28
|
+
licenseSpdxIds= licenseSpdxIds.filter( id => id !== license );
|
|
29
|
+
licenseSpdxIds.unshift(license);
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return licenseSpdxIds;
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.warn(`Warning: could not fetch license list. Using fallbacks.`);
|
|
36
|
+
const licenses = config.FALLBACK_LICENSES.push(config.ALL_RIGHTS_RESERVED_LICENSE)
|
|
37
|
+
licenses.push(config.OTHER_OPTION);
|
|
38
|
+
return licenses;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Fetch specific license information from GitHub
|
|
44
|
+
* @param {string} spdxId - The SPDX ID of the license
|
|
45
|
+
* @returns {Promise<string> | null} The license text
|
|
46
|
+
*/
|
|
47
|
+
export async function getLicenseText(spdxId) {
|
|
48
|
+
if (spdxId === 'all-rights-reserved') {
|
|
49
|
+
return config.ARR_LICENSE_TEXT;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
const url = config.GITHUB_LICENSE_ENDPOINT(spdxId.toLowerCase());
|
|
53
|
+
const response = await fetch(url);
|
|
54
|
+
if (!response.ok) {
|
|
55
|
+
const errorText = await response.text();
|
|
56
|
+
throw new Error(`GitHub API error (${response.status}): ${errorText}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const json = await response.json();
|
|
60
|
+
if (json.body) {
|
|
61
|
+
return json.body;
|
|
62
|
+
} else {
|
|
63
|
+
throw new Error();
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.warn(`Warning: could not find license text for: ${spdxId}`);
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
package/src/modpack-lock.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { generateLockfile, generateReadmeFiles, generateGitignoreRules } from './generate_lockfile.js';
|
|
2
2
|
import generateJson from './generate_json.js';
|
|
3
|
-
import
|
|
3
|
+
import generateLicense from './generate_license.js';
|
|
4
|
+
import { promptUserForInfo } from './modpack_info.js';
|
|
4
5
|
import { getModpackInfo, getLockfile } from './directory_scanning.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* @typedef {import('./config/types.js').ModpackInfo} ModpackInfo
|
|
8
9
|
* @typedef {import('./config/types.js').Options} Options
|
|
10
|
+
* @typedef {import('./config/types.js').InitOptions} InitOptions
|
|
9
11
|
* @typedef {import('./config/types.js').Lockfile} Lockfile
|
|
10
12
|
*/
|
|
11
13
|
|
|
@@ -19,7 +21,7 @@ import { getModpackInfo, getLockfile } from './directory_scanning.js';
|
|
|
19
21
|
* Generate the modpack files (lockfile and JSON)
|
|
20
22
|
* @param {ModpackInfo} modpackInfo - The modpack information
|
|
21
23
|
* @param {string} directory - The directory to generate the files in
|
|
22
|
-
* @param {Options} options - The options object
|
|
24
|
+
* @param {Options | InitOptions } options - The options object
|
|
23
25
|
* @returns {Promise<Lockfile>} The lockfile object
|
|
24
26
|
*/
|
|
25
27
|
async function generateModpackFiles(modpackInfo, directory, options = {}) {
|
|
@@ -28,4 +30,4 @@ async function generateModpackFiles(modpackInfo, directory, options = {}) {
|
|
|
28
30
|
return lockfile;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
|
-
export { generateModpackFiles, generateJson, generateLockfile, generateGitignoreRules, generateReadmeFiles, getModpackInfo, getLockfile, promptUserForInfo };
|
|
33
|
+
export { generateModpackFiles, generateJson, generateLockfile, generateGitignoreRules, generateReadmeFiles, generateLicense, getModpackInfo, getLockfile, promptUserForInfo };
|