modpack-lock 0.3.1 → 0.4.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 +37 -0
- package/package.json +1 -1
- package/src/cli.js +124 -30
- package/src/config/defaults.js +8 -0
- package/src/config/index.js +1 -0
- package/src/modpack_info.js +38 -39
package/README.md
CHANGED
|
@@ -18,6 +18,8 @@ This script generates a `modpack.lock` file in the current directory containing
|
|
|
18
18
|
|
|
19
19
|
The lockfile could also serve as a basis for restoring modpack contents after cloning the repository to a new machine.
|
|
20
20
|
|
|
21
|
+
Using the `scripts` field in `modpack.json`, you can also define reusable, tracked shell commands for common modpack tasks (like publishing, generating assets or CI/CD workflows).
|
|
22
|
+
|
|
21
23
|
## Installation
|
|
22
24
|
|
|
23
25
|
To install the script globally with `npm`:
|
|
@@ -115,6 +117,38 @@ INFORMATION
|
|
|
115
117
|
--help display help for modpack-lock init
|
|
116
118
|
```
|
|
117
119
|
|
|
120
|
+
### Running Scripts
|
|
121
|
+
|
|
122
|
+
To run a script defined in `modpack.json` run:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
modpack-lock run <script>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
This command takes the name of the script as its first argument. Use the `-f` option to specify a different path to the modpack directory. For debug logging, use the `-D` option.
|
|
129
|
+
|
|
130
|
+
To pass additional arguments and options to the script, write them after a `--` separator:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
modpack-lock run <script> -- [options] <args>
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The `scripts` field in `modpack.json` is a key-value pair of script names and their corresponding shell commands. The `scripts` field is optional and is omitted by default.
|
|
137
|
+
|
|
138
|
+
```text
|
|
139
|
+
Usage: modpack-lock run [options] <script>
|
|
140
|
+
|
|
141
|
+
Run a script (shell command) defined in modpack.json's 'scripts' object
|
|
142
|
+
|
|
143
|
+
Arguments:
|
|
144
|
+
script The name of the script to run
|
|
145
|
+
|
|
146
|
+
Options:
|
|
147
|
+
-f, --folder <path> Path to the modpack directory
|
|
148
|
+
-D, --debug Debug mode -- show more information about how the command is being parsed
|
|
149
|
+
-h, --help display help for modpack-lock run
|
|
150
|
+
```
|
|
151
|
+
|
|
118
152
|
> [!TIP]
|
|
119
153
|
>
|
|
120
154
|
> You can run this script as a pre-commit hook to ensure that the modpack lockfile is up to date before committing your changes to your repository.
|
|
@@ -199,6 +233,9 @@ If created via `modpack-lock init`, the JSON file combines your modpack metadata
|
|
|
199
233
|
"resourcepacks": [ ... ],
|
|
200
234
|
"datapacks": [ ... ],
|
|
201
235
|
"shaderpacks": [ ... ]
|
|
236
|
+
},
|
|
237
|
+
"scripts": {
|
|
238
|
+
"example": "echo 'example script'"
|
|
202
239
|
}
|
|
203
240
|
}
|
|
204
241
|
```
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env NODE_OPTIONS=--no-warnings node
|
|
2
2
|
|
|
3
3
|
import { Command } from 'commander';
|
|
4
|
-
import path from 'path';
|
|
5
4
|
import slugify from 'slugify';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { spawn } from 'child_process';
|
|
6
7
|
import {generateLockfile} from './generate_lockfile.js';
|
|
7
|
-
import generateJson from './generate_json.js';
|
|
8
8
|
import { generateModpackFiles } from './modpack-lock.js';
|
|
9
9
|
import promptUserForInfo from './modpack_info.js';
|
|
10
10
|
import { getModpackInfo } from './directory_scanning.js';
|
|
@@ -43,6 +43,28 @@ function restoreConsole() {
|
|
|
43
43
|
console.error = originalLogs.error;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Merge modpack info with priority: options > existingInfo > defaults
|
|
48
|
+
* Preserves all fields from existingInfo
|
|
49
|
+
*/
|
|
50
|
+
function mergeModpackInfo(existingInfo, options, defaults) {
|
|
51
|
+
const result = {};
|
|
52
|
+
for (const [key, defaultValue] of Object.entries(defaults)) {
|
|
53
|
+
result[key] = options[key] || existingInfo?.[key] || defaultValue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Then, add any fields from existingInfo that aren't in defaults
|
|
57
|
+
if (existingInfo) {
|
|
58
|
+
for (const [key, value] of Object.entries(existingInfo)) {
|
|
59
|
+
if (!(key in defaults)) {
|
|
60
|
+
result[key] = value;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
|
|
46
68
|
modpackLock
|
|
47
69
|
.name(pkg.name)
|
|
48
70
|
.description(pkg.description)
|
|
@@ -101,31 +123,36 @@ modpackLock.command('init')
|
|
|
101
123
|
.option('--targetModloaderVersion <targetModloaderVersion>', 'Target modloader version')
|
|
102
124
|
.option('--targetMinecraftVersion <targetMinecraftVersion>', 'Target Minecraft version; required')
|
|
103
125
|
.optionsGroup("INFORMATION")
|
|
104
|
-
.helpOption("--help", `display help for ${pkg.name} init`)
|
|
126
|
+
.helpOption("-h, --help", `display help for ${pkg.name} init`)
|
|
105
127
|
.action(async (options) => {
|
|
106
128
|
const currDir = options.folder || process.cwd();
|
|
107
129
|
|
|
130
|
+
let existingInfo = await getModpackInfo(currDir);
|
|
131
|
+
|
|
108
132
|
if (options.noninteractive) {
|
|
109
133
|
quietConsole();
|
|
110
|
-
if (!options.author || !options.modloader || !options.targetMinecraftVersion) {
|
|
134
|
+
if ( (!options.author && !existingInfo?.author) || (!options.modloader && !existingInfo?.modloader) || (!options.targetMinecraftVersion && !existingInfo?.targetMinecraftVersion)) {
|
|
111
135
|
console.error('Error: Must provide options for required fields');
|
|
112
136
|
process.exitCode = 1;
|
|
113
137
|
return;
|
|
114
138
|
} else {
|
|
115
|
-
const
|
|
116
|
-
const
|
|
117
|
-
name:
|
|
118
|
-
version:
|
|
119
|
-
id:
|
|
120
|
-
description:
|
|
121
|
-
author: options.author,
|
|
122
|
-
projectUrl:
|
|
123
|
-
sourceUrl:
|
|
124
|
-
license:
|
|
125
|
-
modloader: options.modloader,
|
|
126
|
-
targetModloaderVersion:
|
|
127
|
-
targetMinecraftVersion: options.targetMinecraftVersion,
|
|
139
|
+
const defaultName = path.basename(currDir);
|
|
140
|
+
const defaults = {
|
|
141
|
+
name: defaultName,
|
|
142
|
+
version: config.DEFAULT_MODPACK_VERSION,
|
|
143
|
+
id: defaultName,
|
|
144
|
+
description: '',
|
|
145
|
+
author: options.author, // Required, no default
|
|
146
|
+
projectUrl: '',
|
|
147
|
+
sourceUrl: '',
|
|
148
|
+
license: '',
|
|
149
|
+
modloader: options.modloader, // Required, no default
|
|
150
|
+
targetModloaderVersion: '',
|
|
151
|
+
targetMinecraftVersion: options.targetMinecraftVersion, // Required, no default
|
|
128
152
|
};
|
|
153
|
+
|
|
154
|
+
const modpackInfo = mergeModpackInfo(existingInfo, options, defaults);
|
|
155
|
+
modpackInfo.id = slugify(modpackInfo.id, config.SLUGIFY_OPTIONS);
|
|
129
156
|
try {
|
|
130
157
|
await generateModpackFiles(modpackInfo, currDir, { dryRun: false });
|
|
131
158
|
} catch (error) {
|
|
@@ -138,19 +165,23 @@ modpackLock.command('init')
|
|
|
138
165
|
console.log("\nSee `modpack-lock init --help` for definitive documentation on these fields and exactly what they do.\n");
|
|
139
166
|
console.log("Press ^C at any time to quit.\n");
|
|
140
167
|
try {
|
|
141
|
-
const
|
|
142
|
-
name:
|
|
143
|
-
version:
|
|
144
|
-
id:
|
|
145
|
-
description:
|
|
146
|
-
author:
|
|
147
|
-
projectUrl:
|
|
148
|
-
sourceUrl:
|
|
149
|
-
license:
|
|
150
|
-
modloader:
|
|
151
|
-
targetModloaderVersion:
|
|
152
|
-
targetMinecraftVersion:
|
|
153
|
-
}
|
|
168
|
+
const defaults = {
|
|
169
|
+
name: path.basename(currDir),
|
|
170
|
+
version: config.DEFAULT_MODPACK_VERSION,
|
|
171
|
+
id: undefined,
|
|
172
|
+
description: undefined,
|
|
173
|
+
author: undefined,
|
|
174
|
+
projectUrl: undefined,
|
|
175
|
+
sourceUrl: undefined,
|
|
176
|
+
license: config.DEFAULT_MODPACK_LICENSE,
|
|
177
|
+
modloader: undefined,
|
|
178
|
+
targetModloaderVersion: undefined,
|
|
179
|
+
targetMinecraftVersion: undefined,
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const modpackInfo = await promptUserForInfo(
|
|
183
|
+
mergeModpackInfo(existingInfo, options, defaults)
|
|
184
|
+
);
|
|
154
185
|
|
|
155
186
|
await generateModpackFiles(modpackInfo, currDir, { dryRun: false });
|
|
156
187
|
} catch (error) {
|
|
@@ -160,6 +191,69 @@ modpackLock.command('init')
|
|
|
160
191
|
}
|
|
161
192
|
});
|
|
162
193
|
|
|
194
|
+
modpackLock.command('run')
|
|
195
|
+
.description(`Run a script (shell command) defined in ${config.MODPACK_JSON_NAME}\'s \'scripts\' object`)
|
|
196
|
+
.argument('<script>', 'The name of the script to run')
|
|
197
|
+
.option('-f, --folder <path>', 'Path to the modpack directory')
|
|
198
|
+
.option('-D, --debug', 'Debug mode -- show more information about how the command is being parsed')
|
|
199
|
+
.helpOption("-h, --help", `display help for ${pkg.name} run`)
|
|
200
|
+
.allowExcessArguments(true)
|
|
201
|
+
.allowUnknownOption(true)
|
|
202
|
+
.action(async (script, options, command) => {
|
|
203
|
+
try {
|
|
204
|
+
if (options.debug) {
|
|
205
|
+
console.log("COMMAND:", command);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const currDir = options.folder || process.cwd();
|
|
209
|
+
const modpackInfo = await getModpackInfo(currDir);
|
|
210
|
+
|
|
211
|
+
// verify neccecary files and information exist
|
|
212
|
+
if (!modpackInfo) {
|
|
213
|
+
throw new Error('No modpack.json file found');
|
|
214
|
+
}
|
|
215
|
+
if (!modpackInfo.scripts) {
|
|
216
|
+
throw new Error('No scripts defined in modpack.json');
|
|
217
|
+
}
|
|
218
|
+
if (!modpackInfo.scripts[script]) {
|
|
219
|
+
throw new Error(`Script ${script} not found in modpack.json`);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// build the full command
|
|
223
|
+
const scriptCommand = modpackInfo.scripts[script];
|
|
224
|
+
const args = command.args ? command.args.slice(1) : [];
|
|
225
|
+
const fullCommand = `${scriptCommand} ${args.join(' ')}`;
|
|
226
|
+
|
|
227
|
+
// debug logging
|
|
228
|
+
if (options.debug) {
|
|
229
|
+
console.log("CURR DIR:", currDir);
|
|
230
|
+
console.log("OPTIONS:", options);
|
|
231
|
+
console.log("SCRIPT:", script);
|
|
232
|
+
console.log("SCRIPT COMMAND:", scriptCommand);
|
|
233
|
+
console.log("ARGS:", args);
|
|
234
|
+
console.log("FULL COMMAND:", fullCommand);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// spawn the command
|
|
238
|
+
const child = spawn(fullCommand, [], {
|
|
239
|
+
shell: true,
|
|
240
|
+
stdio: 'inherit',
|
|
241
|
+
cwd: currDir
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// preserve exit code on completion
|
|
245
|
+
const exitCode = await new Promise((resolve) => {
|
|
246
|
+
child.on('close', (code) => {
|
|
247
|
+
resolve(code || 0);
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
process.exitCode = exitCode;
|
|
251
|
+
} catch (error) {
|
|
252
|
+
console.error('Error:', error.message);
|
|
253
|
+
process.exitCode = 1;
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
|
|
163
257
|
modpackLock.parseAsync().catch((error) => {
|
|
164
258
|
console.error('Error:', error);
|
|
165
259
|
process.exit(1);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const DEFAULT_MODPACK_VERSION = '1.0.0';
|
|
2
|
+
export const DEFAULT_MODPACK_LICENSE = 'MIT';
|
|
3
|
+
export const DEFAULT_PROJECT_URL = (id) => {
|
|
4
|
+
return `https://modrinth.com/modpack/${id}`;
|
|
5
|
+
};
|
|
6
|
+
export const DEFAULT_SOURCE_URL = (id, author) => {
|
|
7
|
+
return `https://github.com/${author}/${id}`;
|
|
8
|
+
};
|
package/src/config/index.js
CHANGED
package/src/modpack_info.js
CHANGED
|
@@ -33,64 +33,63 @@ export default async function promptUserForInfo(defaults = {}) {
|
|
|
33
33
|
return validateNotEmpty(value, 'Name');
|
|
34
34
|
},
|
|
35
35
|
});
|
|
36
|
-
let
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return validateNotEmpty(value, 'Version');
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
|
|
47
|
-
{
|
|
48
|
-
type: 'text',
|
|
49
|
-
name: 'id',
|
|
50
|
-
message: 'Modpack slug/ID',
|
|
51
|
-
initial: slugify(defaults.id || name.name, config.SLUGIFY_OPTIONS),
|
|
52
|
-
validate: (value) => {
|
|
53
|
-
return validateNotEmpty(value, 'ID');
|
|
54
|
-
},
|
|
36
|
+
let version = await prompts({
|
|
37
|
+
type: 'text',
|
|
38
|
+
name: 'version',
|
|
39
|
+
message: 'Modpack version',
|
|
40
|
+
initial: defaults.version || config.DEFAULT_MODPACK_VERSION,
|
|
41
|
+
validate: (value) => {
|
|
42
|
+
return validateNotEmpty(value, 'Version');
|
|
55
43
|
},
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
44
|
+
});
|
|
45
|
+
let id = await prompts({
|
|
46
|
+
type: 'text',
|
|
47
|
+
name: 'id',
|
|
48
|
+
message: 'Modpack slug/ID',
|
|
49
|
+
initial: slugify(defaults.id || name.name, config.SLUGIFY_OPTIONS),
|
|
50
|
+
validate: (value) => {
|
|
51
|
+
return validateNotEmpty(value, 'ID');
|
|
61
52
|
},
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
53
|
+
});
|
|
54
|
+
let description = await prompts({
|
|
55
|
+
type: 'text',
|
|
56
|
+
name: 'description',
|
|
57
|
+
message: 'Modpack description',
|
|
58
|
+
initial: defaults.description,
|
|
59
|
+
});
|
|
60
|
+
let author = await prompts({
|
|
61
|
+
type: 'text',
|
|
62
|
+
name: 'author',
|
|
63
|
+
message: 'Modpack author',
|
|
64
|
+
initial: defaults.author,
|
|
65
|
+
validate: (value) => {
|
|
66
|
+
return validateNotEmpty(value, 'Author');
|
|
70
67
|
},
|
|
68
|
+
});
|
|
69
|
+
let answers = await prompts([
|
|
71
70
|
{
|
|
72
71
|
type: 'text',
|
|
73
72
|
name: 'projectUrl',
|
|
74
73
|
message: 'Modpack URL',
|
|
75
|
-
initial: defaults.projectUrl ||
|
|
74
|
+
initial: defaults.projectUrl || config.DEFAULT_PROJECT_URL(id.id),
|
|
76
75
|
},
|
|
77
76
|
{
|
|
78
77
|
type: 'text',
|
|
79
78
|
name: 'sourceUrl',
|
|
80
79
|
message: 'Modpack source code URL',
|
|
81
|
-
initial: defaults.sourceUrl ||
|
|
80
|
+
initial: defaults.sourceUrl || config.DEFAULT_SOURCE_URL(id.id, author.author),
|
|
82
81
|
},
|
|
83
82
|
{
|
|
84
83
|
type: 'text',
|
|
85
84
|
name: 'license',
|
|
86
85
|
message: 'Modpack license',
|
|
87
|
-
initial: defaults.license ||
|
|
86
|
+
initial: defaults.license || config.DEFAULT_MODPACK_LICENSE,
|
|
88
87
|
},
|
|
89
88
|
{
|
|
90
89
|
type: 'autocomplete',
|
|
91
90
|
name: 'modloader',
|
|
92
91
|
message: 'Modpack modloader',
|
|
93
|
-
initial: defaults.modloader
|
|
92
|
+
initial: defaults.modloader,
|
|
94
93
|
choices: [
|
|
95
94
|
{ title: 'fabric' },
|
|
96
95
|
{ title: 'forge' },
|
|
@@ -114,20 +113,20 @@ export default async function promptUserForInfo(defaults = {}) {
|
|
|
114
113
|
type: 'text',
|
|
115
114
|
name: 'targetModloaderVersion',
|
|
116
115
|
message: 'Target modloader version',
|
|
117
|
-
initial: defaults.targetModloaderVersion
|
|
116
|
+
initial: defaults.targetModloaderVersion,
|
|
118
117
|
},
|
|
119
118
|
{
|
|
120
119
|
type: 'text',
|
|
121
120
|
name: 'targetMinecraftVersion',
|
|
122
121
|
message: 'Target Minecraft version',
|
|
123
|
-
initial: defaults.targetMinecraftVersion
|
|
122
|
+
initial: defaults.targetMinecraftVersion,
|
|
124
123
|
validate: (value) => {
|
|
125
124
|
return validateNotEmpty(value, 'Minecraft Version');
|
|
126
125
|
},
|
|
127
126
|
}
|
|
128
127
|
]);
|
|
129
128
|
|
|
130
|
-
let modpackInfo = {...name, ...answers};
|
|
129
|
+
let modpackInfo = {...name, ...version, ...id, ...description, ...author, ...answers};
|
|
131
130
|
if (Object.keys(modpackInfo).length < 11) {
|
|
132
131
|
console.warn('Modpack initialization was interrupted');
|
|
133
132
|
process.exit(1);
|