modpack-lock 0.3.2 → 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.
Files changed (3) hide show
  1. package/README.md +37 -0
  2. package/package.json +1 -1
  3. package/src/cli.js +78 -3
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "modpack-lock",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "description": "Creates a modpack lockfile for files hosted on Modrinth (mods, resource packs, shaders and datapacks)",
5
5
  "bugs": {
6
6
  "url": "https://github.com/nickesc/modpack-lock/issues"
package/src/cli.js CHANGED
@@ -1,8 +1,9 @@
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
8
  import { generateModpackFiles } from './modpack-lock.js';
8
9
  import promptUserForInfo from './modpack_info.js';
@@ -44,12 +45,23 @@ function restoreConsole() {
44
45
 
45
46
  /**
46
47
  * Merge modpack info with priority: options > existingInfo > defaults
48
+ * Preserves all fields from existingInfo
47
49
  */
48
50
  function mergeModpackInfo(existingInfo, options, defaults) {
49
51
  const result = {};
50
52
  for (const [key, defaultValue] of Object.entries(defaults)) {
51
53
  result[key] = options[key] || existingInfo?.[key] || defaultValue;
52
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
+
53
65
  return result;
54
66
  }
55
67
 
@@ -111,7 +123,7 @@ modpackLock.command('init')
111
123
  .option('--targetModloaderVersion <targetModloaderVersion>', 'Target modloader version')
112
124
  .option('--targetMinecraftVersion <targetMinecraftVersion>', 'Target Minecraft version; required')
113
125
  .optionsGroup("INFORMATION")
114
- .helpOption("--help", `display help for ${pkg.name} init`)
126
+ .helpOption("-h, --help", `display help for ${pkg.name} init`)
115
127
  .action(async (options) => {
116
128
  const currDir = options.folder || process.cwd();
117
129
 
@@ -119,7 +131,7 @@ modpackLock.command('init')
119
131
 
120
132
  if (options.noninteractive) {
121
133
  quietConsole();
122
- if (!options.author || !options.modloader || !options.targetMinecraftVersion) {
134
+ if ( (!options.author && !existingInfo?.author) || (!options.modloader && !existingInfo?.modloader) || (!options.targetMinecraftVersion && !existingInfo?.targetMinecraftVersion)) {
123
135
  console.error('Error: Must provide options for required fields');
124
136
  process.exitCode = 1;
125
137
  return;
@@ -179,6 +191,69 @@ modpackLock.command('init')
179
191
  }
180
192
  });
181
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
+
182
257
  modpackLock.parseAsync().catch((error) => {
183
258
  console.error('Error:', error);
184
259
  process.exit(1);