hackmud-script-manager 0.19.1-3ef8894 → 0.19.1-531ddb2

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/bin/hsm.js CHANGED
@@ -1,7 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import { Cache } from "@samual/lib/Cache"
3
3
  import { assert } from "@samual/lib/assert"
4
+ import { catchError } from "@samual/lib/catchError"
4
5
  import { countHackmudCharacters } from "@samual/lib/countHackmudCharacters"
6
+ import { getDeepObjectProperty } from "@samual/lib/getDeepObjectProperty"
7
+ import { isRecord } from "@samual/lib/isRecord"
8
+ import { setDeepObjectProperty } from "@samual/lib/setDeepObjectProperty"
5
9
  import { writeFilePersistent } from "@samual/lib/writeFilePersistent"
6
10
  import { readFile, writeFile, mkdir, rmdir } from "fs/promises"
7
11
  import { homedir } from "os"
@@ -10,6 +14,7 @@ import { supportedExtensions } from "../constants.js"
10
14
  import { generateTypeDeclaration } from "../generateTypeDeclaration.js"
11
15
  import { pull } from "../pull.js"
12
16
  import { syncMacros } from "../syncMacros.js"
17
+ import "@samual/lib/readDirectoryWithStats"
13
18
  import "@samual/lib/copyFilePersistent"
14
19
  const configDirectoryPath = resolve(homedir(), ".config"),
15
20
  configFilePath = resolve(configDirectoryPath, "hsm.json"),
@@ -20,119 +25,7 @@ const configDirectoryPath = resolve(homedir(), ".config"),
20
25
  for (const char of user) hash += (hash >> 1) + hash + "xi1_8ratvsw9hlbgm02y5zpdcn7uekof463qj".indexOf(char) + 1
21
26
  return [colourJ, colourK, colourM, colourW, colourL, colourB][hash % 6](user)
22
27
  }),
23
- logNeedHackmudPathMessage = () =>
24
- console.error(
25
- colourS(
26
- `${colourD("You need to set hackmudPath in config before you can use this command")}\n\n${colourA("To fix this:")}\nOpen hackmud and run "${colourC("#dir")}"\nThis will open a file browser and print your hackmud user's script directory\nGo up 2 directories and then copy the path\nThen in a terminal run "${colourC("hsm")} ${colourL("config set")} ${colourV("hackmudPath")} ${colourB("<the path you copied>")}"`
27
- )
28
- ),
29
- logHelp = () => {
30
- const pushCommandDescription = "Push scripts from a directory to hackmud user's scripts directories",
31
- mangleNamesOptionDescription =
32
- "Reduce character count further but lose function names in error call stacks",
33
- forceQuineCheatsOptionDescription = `Force quine cheats on. Use ${colourN("--force-quine-cheats")}=${colourV("false")} to force off.`
34
- console.log(colourN("Version") + colourS(": ") + colourV("0.19.1-3ef8894"))
35
- switch (commands[0]) {
36
- case "config":
37
- switch (commands[1]) {
38
- case "get":
39
- console.log(
40
- `\n${colourJ("Retrieve a value from the config file")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(`${commands[0]} ${commands[1]}`)} ${colourB("<key>")}`
41
- )
42
- break
43
- case "set":
44
- console.log(
45
- `\n${colourJ("Assign a value to the config file")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(`${commands[0]} ${commands[1]}`)} ${colourB("<key> <value>")}`
46
- )
47
- break
48
- case "delete":
49
- console.log(
50
- `\n${colourJ("Remove a key and value from the config file")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(`${commands[0]} ${commands[1]}`)} ${colourB("<key>")}`
51
- )
52
- break
53
- default:
54
- console.log(
55
- colourS(
56
- `${colourN("Config path")}: ${colourV(configFilePath)}\n\n${colourJ("Modify the config file")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(commands[0] + " get")} ${colourB("<key>")}\n Retrieve a value from the config file\n${colourC("hsm")} ${colourL(commands[0] + " set")} ${colourB("<key> <value>")}\n Assign a value to the config file\n${colourC("hsm")} ${colourL(commands[0] + " delete")} ${colourB("<key>")}\n Remove a key and value from the config file`
57
- )
58
- )
59
- }
60
- break
61
- case "push":
62
- console.log(
63
- colourS(
64
- `\n${colourJ(pushCommandDescription)}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourB("<directory> [<script user>.<script name>...]")}\n\n${colourA("Options:")}\n${colourN("--no-minify")}\n Skip minification to produce a "readable" script\n${colourN("--mangle-names")}\n ${mangleNamesOptionDescription}\n${colourN("--force-quine-cheats")}\n ${forceQuineCheatsOptionDescription}`
65
- )
66
- )
67
- break
68
- case "dev":
69
- case "watch":
70
- console.log(
71
- colourS(
72
- `\n${colourJ("Watch a directory and push a script when modified")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourB("<directory> [<script user>.<script name>...]")}\n\n${colourA("Options:")}\n${colourN("--no-minify")}\n Skip minification to produce a "readable" script\n${colourN("--mangle-names")}\n ${mangleNamesOptionDescription}\n${colourN("--type-declaration-path")}=${colourB("<path>")}\n Path to generate a type declaration file for the scripts\n${colourN("--force-quine-cheats")}\n ${forceQuineCheatsOptionDescription}`
73
- )
74
- )
75
- break
76
- case "pull":
77
- console.log(
78
- colourS(
79
- `\n${colourJ("Pull a script a from a hackmud user's script directory")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourB("<script user>")}${colourV(".")}${colourB("<script name>")}`
80
- )
81
- )
82
- break
83
- case "minify":
84
- case "golf":
85
- console.log(
86
- colourS(
87
- `\n${colourJ("Minify a script file on the spot")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourB("<target> [output path]")}\n\n${colourA("Options:")}\n${colourN("--no-minify")}\n Skip minification to produce a "readable" script\n${colourN("--mangle-names")}\n ${mangleNamesOptionDescription}\n${colourN("--force-quine-cheats")}\n ${forceQuineCheatsOptionDescription}\n${colourN("--watch")}\n Watch for changes`
88
- )
89
- )
90
- break
91
- case "generate-type-declaration":
92
- case "gen-type-declaration":
93
- case "gen-dts":
94
- case "gen-types":
95
- console.log(
96
- colourS(
97
- `${colourJ("Generate a type declaration file for a directory of scripts")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourB("<directory> [output path]")}`
98
- )
99
- )
100
- break
101
- case "sync-macros":
102
- console.log("\n" + colourJ("Sync macros across all hackmud users"))
103
- break
104
- default:
105
- console.log(
106
- colourS(
107
- `\n${colourJ("Hackmud Script Manager")}\n\n${colourA("Commands:")}\n${colourL("push")}\n ${pushCommandDescription}\n${colourL("dev")}\n Watch a directory and push a script when modified\n${colourL("golf")}\n Minify a script file on the spot\n${colourL("gen-dts")}\n Generate a type declaration file for a directory of scripts\n${colourL("sync-macros")}\n Sync macros across all hackmud users\n${colourL("config")}\n Modify and view the config file\n${colourL("pull")}\n Pull a script a from a hackmud user's script directory`
108
- )
109
- )
110
- }
111
- },
112
- exploreObject = (object, keys, createPath = !1) => {
113
- for (const key of keys)
114
- object =
115
- createPath ?
116
- "object" == typeof object[key] ?
117
- object[key]
118
- : (object[key] = {})
119
- : object?.[key]
120
- return object
121
- },
122
- logInfo = ({ file, users, minLength, error }, hackmudPath) => {
123
- error ?
124
- logError(`error "${chalk.bold(error.message)}" in ${chalk.bold(file)}`)
125
- : console.log(
126
- `pushed ${chalk.bold(file)} to ${users.map(user => chalk.bold(userColours.get(user))).join(", ")} | ${chalk.bold(minLength + "")} chars | ${chalk.bold(resolve(hackmudPath, users[0], "scripts", basename(file, extname(file))) + ".js")}`
127
- )
128
- },
129
- log = message => {
130
- console.log(colourS(message))
131
- },
132
- logError = message => {
133
- console.error(colourD(message))
134
- process.exitCode = 1
135
- }
28
+ log = message => console.log(colourS(message))
136
29
  for (const argument of process.argv.slice(2))
137
30
  if ("-" == argument[0]) {
138
31
  const [key, valueRaw] = argument.split("=")
@@ -149,20 +42,14 @@ for (const argument of process.argv.slice(2))
149
42
  else for (const option of key.slice(1)) options.set(option, value)
150
43
  } else commands.push(argument)
151
44
  if ("v" == commands[0] || "version" == commands[0] || options.get("version") || options.get("v")) {
152
- console.log("0.19.1-3ef8894")
45
+ console.log("0.19.1-531ddb2")
153
46
  process.exit()
154
47
  }
155
48
  let configDidNotExist = !1
156
49
  const configPromise = readFile(configFilePath, { encoding: "utf-8" }).then(
157
50
  configFile => {
158
- let temporaryConfig
159
- try {
160
- temporaryConfig = JSON.parse(configFile)
161
- } catch {
162
- log("Config file was corrupted, resetting")
163
- return {}
164
- }
165
- if (!temporaryConfig || "object" != typeof temporaryConfig) {
51
+ const [temporaryConfig, error] = catchError(() => JSON.parse(configFile))
52
+ if (error || !isRecord(temporaryConfig)) {
166
53
  log("Config file was corrupted, resetting")
167
54
  return {}
168
55
  }
@@ -199,15 +86,13 @@ if (options.get("help") || options.get("h")) {
199
86
  process.exit()
200
87
  }
201
88
  let autoExit = !0
89
+ const getDefaultHackmudPath = () =>
90
+ "win32" == process.platform ? resolve(process.env.APPDATA, "hackmud") : resolve(homedir(), ".config/hackmud")
202
91
  switch (commands[0]) {
203
92
  case "push":
204
93
  {
205
- const { hackmudPath } = await configPromise
206
- if (!hackmudPath) {
207
- logNeedHackmudPathMessage()
208
- break
209
- }
210
- const sourcePath = commands[1]
94
+ const { hackmudPath = getDefaultHackmudPath() } = await configPromise,
95
+ sourcePath = commands[1]
211
96
  if (!sourcePath) {
212
97
  logError("Must provide the directory to push from\n")
213
98
  logHelp()
@@ -275,12 +160,8 @@ switch (commands[0]) {
275
160
  case "dev":
276
161
  case "watch":
277
162
  {
278
- const { hackmudPath } = await configPromise
279
- if (!hackmudPath) {
280
- logNeedHackmudPathMessage()
281
- break
282
- }
283
- const sourcePath = commands[1]
163
+ const { hackmudPath = getDefaultHackmudPath() } = await configPromise,
164
+ sourcePath = commands[1]
284
165
  if (!sourcePath) {
285
166
  logError("Must provide the directory to watch\n")
286
167
  logHelp()
@@ -353,34 +234,24 @@ switch (commands[0]) {
353
234
  break
354
235
  case "pull":
355
236
  {
356
- const { hackmudPath } = await configPromise
357
- if (!hackmudPath) {
358
- logNeedHackmudPathMessage()
359
- break
360
- }
361
- const script = commands[1]
237
+ const { hackmudPath = getDefaultHackmudPath() } = await configPromise,
238
+ script = commands[1]
362
239
  if (!script) {
363
240
  logError("Must provide the script to pull\n")
364
241
  logHelp()
365
242
  break
366
243
  }
367
244
  const sourcePath = commands[2] || "."
368
- try {
369
- await pull(sourcePath, hackmudPath, script)
370
- } catch (error) {
245
+ await pull(sourcePath, hackmudPath, script).catch(error => {
371
246
  console.error(error)
372
247
  logError(`Something went wrong, did you forget to ${colourC("#down")} the script?`)
373
- }
248
+ })
374
249
  }
375
250
  break
376
251
  case "sync-macros":
377
252
  {
378
- const { hackmudPath } = await configPromise
379
- if (!hackmudPath) {
380
- logNeedHackmudPathMessage()
381
- break
382
- }
383
- const { macrosSynced, usersSynced } = await syncMacros(hackmudPath)
253
+ const { hackmudPath = getDefaultHackmudPath() } = await configPromise,
254
+ { macrosSynced, usersSynced } = await syncMacros(hackmudPath)
384
255
  log(`Synced ${macrosSynced} macros to ${usersSynced} users`)
385
256
  }
386
257
  break
@@ -397,16 +268,17 @@ switch (commands[0]) {
397
268
  }
398
269
  const sourcePath = resolve(target),
399
270
  outputPath = commands[2] || "./player.d.ts",
400
- typeDeclaration = await generateTypeDeclaration(sourcePath, (await configPromise).hackmudPath)
271
+ typeDeclaration = await generateTypeDeclaration(
272
+ sourcePath,
273
+ (await configPromise).hackmudPath || getDefaultHackmudPath()
274
+ )
401
275
  let typeDeclarationPath = resolve(outputPath)
402
- try {
403
- await writeFile(typeDeclarationPath, typeDeclaration)
404
- } catch (error) {
405
- assert(error instanceof Error)
276
+ await writeFile(typeDeclarationPath, typeDeclaration).catch(error => {
277
+ assert(error instanceof Error, "src/bin/hsm.ts:365:35")
406
278
  if ("EISDIR" != error.code) throw error
407
279
  typeDeclarationPath = resolve(typeDeclarationPath, "player.d.ts")
408
- await writeFile(typeDeclarationPath, typeDeclaration)
409
- }
280
+ return writeFile(typeDeclarationPath, typeDeclaration)
281
+ })
410
282
  log("Wrote type declaration to " + chalk.bold(typeDeclarationPath))
411
283
  }
412
284
  break
@@ -414,8 +286,14 @@ switch (commands[0]) {
414
286
  switch (commands[1]) {
415
287
  case "get":
416
288
  {
417
- const key = commands[2]
418
- key ? log(exploreObject(await configPromise, key.split("."))) : console.log(await configPromise)
289
+ const key = commands[2],
290
+ config = await configPromise
291
+ if (key) {
292
+ const [value, error] = catchError(() => getDeepObjectProperty(config, key.split(".")))
293
+ error ? logError("Could not get key " + colourV(key))
294
+ : "string" == typeof value ? log(value)
295
+ : console.log(value)
296
+ } else console.log(config)
419
297
  }
420
298
  break
421
299
  case "delete":
@@ -426,14 +304,16 @@ switch (commands[0]) {
426
304
  logHelp()
427
305
  break
428
306
  }
429
- const keyParts = key.split("."),
430
- pathName = keyParts
431
- .map(name => (/^[a-z_$][\w$]*$/i.test(name) ? name : JSON.stringify(name)))
432
- .join("."),
433
- lastKey = keyParts.pop(),
434
- config = await configPromise
435
- delete exploreObject(config, keyParts)?.[lastKey]
436
- log(`Removed ${colourV(pathName)} from config file`)
307
+ const keys = key.split("."),
308
+ lastKey = keys.pop(),
309
+ config = await configPromise,
310
+ object = getDeepObjectProperty(config, keys)
311
+ if (isRecord(object)) {
312
+ delete object[lastKey]
313
+ await updateConfig(config)
314
+ log(`Removed ${colourV(key)} from config file:`)
315
+ console.log(config)
316
+ } else log("Could not delete " + colourV(key))
437
317
  }
438
318
  break
439
319
  case "set":
@@ -445,49 +325,20 @@ switch (commands[0]) {
445
325
  logHelp()
446
326
  break
447
327
  }
448
- const keys = key.split("."),
449
- pathName = keys
450
- .map(name => (/^[a-z_$][\w$]*$/i.test(name) ? name : JSON.stringify(name)))
451
- .join(".")
452
328
  if (!value) {
453
- logError(`Must provide a value for the key ${pathName}\n`)
329
+ logError(`Must provide a value for the key ${colourV(key)}\n`)
454
330
  logHelp()
455
331
  break
456
332
  }
457
- const lastKey = keys.pop(),
458
- config = await configPromise
459
- if (keys.length || "hackmudPath" != lastKey) {
460
- let object = config
461
- for (const key of keys)
462
- if ("object" == typeof object[key]) object = object[key]
463
- else {
464
- object[key] = {}
465
- object = object[key]
466
- }
467
- object[lastKey] = value
468
- } else config.hackmudPath = resolve(value.startsWith("~/") ? homedir() + value.slice(1) : value)
333
+ const config = await configPromise
334
+ setDeepObjectProperty(config, key.split("."), value)
335
+ log(`Set ${colourV(key)} to ${colourV(value)}:`)
469
336
  console.log(config)
470
- await (async config => {
471
- const json = JSON.stringify(config, void 0, "\t")
472
- configDidNotExist && log("Creating config file at " + configFilePath)
473
- await writeFile(configFilePath, json).catch(async error => {
474
- switch (error.code) {
475
- case "EISDIR":
476
- await rmdir(configFilePath)
477
- break
478
- case "ENOENT":
479
- await mkdir(configDirectoryPath)
480
- break
481
- default:
482
- throw error
483
- }
484
- await writeFile(configFilePath, json)
485
- })
486
- })(config)
337
+ await updateConfig(config)
487
338
  }
488
339
  break
489
340
  default:
490
- commands[1] && logError(`Unknown command: ${JSON.stringify(commands[1])}\n`)
341
+ commands[1] && logError(`Unknown command: ${colourL(commands[1])}\n`)
491
342
  logHelp()
492
343
  }
493
344
  break
@@ -554,36 +405,31 @@ switch (commands[0]) {
554
405
  : fileBaseName + ".js"
555
406
  )
556
407
  const golfFile = () =>
557
- readFile(target, { encoding: "utf-8" }).then(
558
- async source => {
559
- const timeStart = performance.now(),
560
- { script, warnings } = await processScript(source, {
561
- minify: !(options.get("no-minify") || options.get("skip-minify")),
562
- scriptUser,
563
- scriptName,
564
- filePath: target,
565
- mangleNames,
566
- forceQuineCheats
567
- }),
568
- timeTook = performance.now() - timeStart
569
- for (const { message, line } of warnings)
570
- log(`Warning "${chalk.bold(message)}" on line ${chalk.bold(line + "")}`)
571
- await writeFilePersistent(outputPath, script)
572
- .catch(async error => {
573
- if (!commands[2] || "EISDIR" != error.code) throw error
574
- outputPath = resolve(outputPath, basename(target, fileExtension) + ".js")
575
- await writeFilePersistent(outputPath, script)
576
- })
577
- .then(
578
- () =>
579
- log(
580
- `Wrote ${chalk.bold(countHackmudCharacters(script))} chars to ${chalk.bold(relative(".", outputPath))} | took ${Math.round(100 * timeTook) / 100}ms`
581
- ),
582
- error => logError(error.message)
408
+ readFile(target, { encoding: "utf-8" }).then(async source => {
409
+ const timeStart = performance.now(),
410
+ { script, warnings } = await processScript(source, {
411
+ minify: !(options.get("no-minify") || options.get("skip-minify")),
412
+ scriptUser,
413
+ scriptName,
414
+ filePath: target,
415
+ mangleNames,
416
+ forceQuineCheats
417
+ }),
418
+ timeTook = performance.now() - timeStart
419
+ for (const { message, line } of warnings)
420
+ log(`Warning "${chalk.bold(message)}" on line ${chalk.bold(line + "")}`)
421
+ await writeFilePersistent(outputPath, script)
422
+ .catch(error => {
423
+ if (!commands[2] || "EISDIR" != error.code) throw error
424
+ outputPath = resolve(outputPath, basename(target, fileExtension) + ".js")
425
+ return writeFilePersistent(outputPath, script)
426
+ })
427
+ .then(() =>
428
+ log(
429
+ `Wrote ${chalk.bold(countHackmudCharacters(script))} chars to ${chalk.bold(relative(".", outputPath))} | took ${Math.round(100 * timeTook) / 100}ms`
583
430
  )
584
- },
585
- error => logError(error.message)
586
- )
431
+ )
432
+ })
587
433
  if (options.get("watch")) {
588
434
  const { watch: watchFile } = await chokidarModule
589
435
  watchFile(target, { awaitWriteFinish: { stabilityThreshold: 100 } })
@@ -594,7 +440,110 @@ switch (commands[0]) {
594
440
  }
595
441
  break
596
442
  default:
597
- commands[0] && logError(`Unknown command: ${JSON.stringify(commands[0])}\n`)
443
+ commands[0] && logError(`Unknown command: ${colourL(commands[0])}\n`)
598
444
  logHelp()
599
445
  }
600
446
  autoExit && process.exit()
447
+ function logHelp() {
448
+ const pushCommandDescription = "Push scripts from a directory to hackmud user's scripts directories",
449
+ forceQuineCheatsOptionDescription = `Force quine cheats on. Use ${colourN("--force-quine-cheats")}=${colourV("false")} to force off`
450
+ console.log(colourN("Version") + colourS(": ") + colourV("0.19.1-531ddb2"))
451
+ switch (commands[0]) {
452
+ case "config":
453
+ switch (commands[1]) {
454
+ case "get":
455
+ console.log(
456
+ `\n${colourJ("Retrieve a value from the config file")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(`${commands[0]} ${commands[1]}`)} ${colourB("<key>")}`
457
+ )
458
+ break
459
+ case "set":
460
+ console.log(
461
+ `\n${colourJ("Assign a value to the config file")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(`${commands[0]} ${commands[1]}`)} ${colourB("<key> <value>")}`
462
+ )
463
+ break
464
+ case "delete":
465
+ console.log(
466
+ `\n${colourJ("Remove a key and value from the config file")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(`${commands[0]} ${commands[1]}`)} ${colourB("<key>")}`
467
+ )
468
+ break
469
+ default:
470
+ console.log(
471
+ colourS(
472
+ `${colourN("Config path")}: ${colourV(configFilePath)}\n\n${colourJ("Modify the config file")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(commands[0] + " get")} ${colourB("<key>")}\n Retrieve a value from the config file\n${colourC("hsm")} ${colourL(commands[0] + " set")} ${colourB("<key> <value>")}\n Assign a value to the config file\n${colourC("hsm")} ${colourL(commands[0] + " delete")} ${colourB("<key>")}\n Remove a key and value from the config file`
473
+ )
474
+ )
475
+ }
476
+ break
477
+ case "dev":
478
+ case "watch":
479
+ case "push":
480
+ console.log(
481
+ colourS(
482
+ `\n${colourJ("push" == commands[0] ? pushCommandDescription : "Watch a directory and push a script when modified")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourB('<directory> ["<script user>.<script name>"...]')}\n\n${colourA("Arguments:")}\n${colourB("<directory>")}\n The source directory containing your scripts\n${colourB("<script user>")}\n A user to push script(s) to. Can be set to wild card (${colourV("*")}) which will try\n and discover users to push to\n${colourB("<script name>")}\n Name of a script to push. Can be set to wild card (${colourV("*")}) to find all scripts\n\n${colourA("Options:")}\n${colourN("--no-minify")}\n Skip minification to produce a "readable" script\n${colourN("--mangle-names")}\n Reduce character count further but lose function names in error call stacks\n${colourN("--force-quine-cheats")}\n ${forceQuineCheatsOptionDescription}\n${"push" == commands[0] ? "" : `${colourN("--type-declaration-path")}=${colourB("<path>")}\n Path to generate a type declaration file for the scripts\n`}\n${colourA("Examples:")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourV("src")}\n\tPushes all scripts found in ${colourV("src")} folder to all users\n${colourC("hsm")} ${colourL(commands[0])} ${colourV("src")} ${colourC("foo")}${colourV(".")}${colourL("bar")}\n Pushes a script named ${colourL("bar")} found in ${colourV("src")} folder to user ${userColours.get("foo")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourV("src")} ${colourC("foo")}${colourV(".")}${colourL("bar")} ${colourC("baz")}${colourV(".")}${colourL("qux")}\n Multiple can be specified.\n${colourC("hsm")} ${colourL(commands[0])} ${colourV("src")} ${colourC("foo")}${colourV(".")}${colourL("*")}\n\tPushes all scripts found in ${colourV("src")} folder to user ${userColours.get("foo")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourV("src")} ${colourC("*")}${colourV(".")}${colourL("foo")}\n\tPushes all scripts named ${colourL("foo")} found in ${colourV("src")} folder to all user\n${colourC("hsm")} ${colourL(commands[0])} ${colourV("src")} ${colourC("*")}${colourV(".")}${colourL("*")}\n\tPushes all scripts found in ${colourV("src")} folder to all users`
483
+ )
484
+ )
485
+ break
486
+ case "pull":
487
+ console.log(
488
+ colourS(
489
+ `\n${colourJ("Pull a script a from a hackmud user's script directory")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourB("<script user>")}${colourV(".")}${colourB("<script name>")}`
490
+ )
491
+ )
492
+ break
493
+ case "minify":
494
+ case "golf":
495
+ console.log(
496
+ colourS(
497
+ `\n${colourJ("Minify a script file on the spot")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourB("<target> [output path]")}\n\n${colourA("Options:")}\n${colourN("--no-minify")}\n Skip minification to produce a "readable" script\n${colourN("--mangle-names")}\n Reduce character count further but lose function names in error call stacks\n${colourN("--force-quine-cheats")}\n ${forceQuineCheatsOptionDescription}\n${colourN("--watch")}\n Watch for changes`
498
+ )
499
+ )
500
+ break
501
+ case "generate-type-declaration":
502
+ case "gen-type-declaration":
503
+ case "gen-dts":
504
+ case "gen-types":
505
+ console.log(
506
+ colourS(
507
+ `${colourJ("Generate a type declaration file for a directory of scripts")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourB("<directory> [output path]")}`
508
+ )
509
+ )
510
+ break
511
+ case "sync-macros":
512
+ console.log("\n" + colourJ("Sync macros across all hackmud users"))
513
+ break
514
+ default:
515
+ console.log(
516
+ colourS(
517
+ `\n${colourJ("Hackmud Script Manager")}\n\n${colourA("Commands:")}\n${colourL("push")}\n ${pushCommandDescription}\n${colourL("dev")}\n Watch a directory and push a script when modified\n${colourL("golf")}\n Minify a script file on the spot\n${colourL("gen-dts")}\n Generate a type declaration file for a directory of scripts\n${colourL("sync-macros")}\n Sync macros across all hackmud users\n${colourL("config")}\n Modify and view the config file\n${colourL("pull")}\n Pull a script a from a hackmud user's script directory`
518
+ )
519
+ )
520
+ }
521
+ }
522
+ async function updateConfig(config) {
523
+ const json = JSON.stringify(config, void 0, "\t")
524
+ configDidNotExist && log("Creating config file at " + configFilePath)
525
+ await writeFile(configFilePath, json).catch(async error => {
526
+ switch (error.code) {
527
+ case "EISDIR":
528
+ await rmdir(configFilePath)
529
+ break
530
+ case "ENOENT":
531
+ await mkdir(configDirectoryPath)
532
+ break
533
+ default:
534
+ throw error
535
+ }
536
+ await writeFile(configFilePath, json)
537
+ })
538
+ }
539
+ function logInfo({ file, users, minLength, error }, hackmudPath) {
540
+ error ?
541
+ logError(`error "${chalk.bold(error.message)}" in ${chalk.bold(file)}`)
542
+ : console.log(
543
+ `pushed ${chalk.bold(file)} to ${users.map(user => chalk.bold(userColours.get(user))).join(", ")} | ${chalk.bold(minLength + "")} chars | ${chalk.bold(resolve(hackmudPath, users[0], "scripts", basename(file, extname(file))) + ".js")}`
544
+ )
545
+ }
546
+ function logError(message) {
547
+ console.error(colourD(message))
548
+ process.exitCode = 1
549
+ }
@@ -1,31 +1,31 @@
1
- import { readdir } from "fs/promises"
1
+ import { readDirectoryWithStats } from "@samual/lib/readDirectoryWithStats"
2
2
  import { basename, resolve } from "path"
3
- const generateTypeDeclaration = async (sourceDirectory, hackmudPath) => {
3
+ async function generateTypeDeclaration(sourceDirectory, hackmudPath) {
4
4
  const users = new Set()
5
5
  if (hackmudPath)
6
- for (const dirent of await readdir(hackmudPath, { withFileTypes: !0 }))
7
- dirent.isFile() && dirent.name.endsWith(".key") && users.add(basename(dirent.name, ".key"))
6
+ for (const { stats, name } of await readDirectoryWithStats(hackmudPath))
7
+ stats.isFile() && name.endsWith(".key") && users.add(basename(name, ".key"))
8
8
  const wildScripts = [],
9
9
  wildAnyScripts = [],
10
10
  allScripts = {},
11
11
  allAnyScripts = {}
12
12
  await Promise.all(
13
- (await readdir(sourceDirectory, { withFileTypes: !0 })).map(async dirent => {
14
- if (dirent.isFile())
15
- dirent.name.endsWith(".ts") ?
16
- dirent.name.endsWith(".d.ts") || wildScripts.push(basename(dirent.name, ".ts"))
17
- : dirent.name.endsWith(".js") && wildAnyScripts.push(basename(dirent.name, ".js"))
18
- else if (dirent.isDirectory()) {
13
+ (await readDirectoryWithStats(sourceDirectory)).map(async ({ stats, name }) => {
14
+ if (stats.isFile())
15
+ name.endsWith(".ts") ?
16
+ name.endsWith(".d.ts") || wildScripts.push(basename(name, ".ts"))
17
+ : name.endsWith(".js") && wildAnyScripts.push(basename(name, ".js"))
18
+ else if (stats.isDirectory()) {
19
19
  const scripts = [],
20
20
  anyScripts = []
21
- allScripts[dirent.name] = scripts
22
- allAnyScripts[dirent.name] = anyScripts
23
- users.add(dirent.name)
24
- for (const file of await readdir(resolve(sourceDirectory, dirent.name), { withFileTypes: !0 }))
25
- file.isFile() &&
26
- (file.name.endsWith(".ts") ?
27
- dirent.name.endsWith(".d.ts") || scripts.push(basename(file.name, ".ts"))
28
- : file.name.endsWith(".js") && anyScripts.push(basename(file.name, ".js")))
21
+ allScripts[name] = scripts
22
+ allAnyScripts[name] = anyScripts
23
+ users.add(name)
24
+ for (const child of await readDirectoryWithStats(resolve(sourceDirectory, name)))
25
+ child.stats.isFile() &&
26
+ (child.name.endsWith(".ts") ?
27
+ name.endsWith(".d.ts") || scripts.push(basename(child.name, ".ts"))
28
+ : child.name.endsWith(".js") && anyScripts.push(basename(child.name, ".js")))
29
29
  }
30
30
  })
31
31
  )
package/index.js CHANGED
@@ -5,7 +5,7 @@ export { pull } from "./pull.js"
5
5
  export { push } from "./push.js"
6
6
  export { syncMacros } from "./syncMacros.js"
7
7
  export { watch } from "./watch.js"
8
- import "fs/promises"
8
+ import "@samual/lib/readDirectoryWithStats"
9
9
  import "path"
10
10
  import "@babel/generator"
11
11
  import "@babel/parser"
@@ -47,4 +47,5 @@ import "@samual/lib/clearObject"
47
47
  import "@samual/lib/copyFilePersistent"
48
48
  import "@samual/lib/Cache"
49
49
  import "@samual/lib/writeFilePersistent"
50
+ import "fs/promises"
50
51
  import "chokidar"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hackmud-script-manager",
3
- "version": "0.19.1-3ef8894",
3
+ "version": "0.19.1-531ddb2",
4
4
  "description": "Script manager for game hackmud, with minification, TypeScript support, and player script type definition generation.",
5
5
  "keywords": [
6
6
  "api",
@@ -59,7 +59,7 @@
59
59
  "@rollup/plugin-commonjs": "^25.0.7",
60
60
  "@rollup/plugin-json": "^6.1.0",
61
61
  "@rollup/plugin-node-resolve": "^15.2.3",
62
- "@samual/lib": "^0.10.1",
62
+ "@samual/lib": "0.10.2-e64c5bc",
63
63
  "acorn": "^8.11.3",
64
64
  "chalk": "^5.3.0",
65
65
  "chokidar": "^3.6.0",
@@ -69,17 +69,15 @@
69
69
  "rollup": "^4.14.2",
70
70
  "terser": "^5.30.3"
71
71
  },
72
- "engines": {
73
- "node": "^18 || >=20",
74
- "pnpm": "^9.0.1"
75
- },
76
72
  "type": "module",
77
73
  "exports": {
78
74
  "./*": "./*.js",
79
75
  "./*.js": "./*.js"
80
76
  },
81
77
  "bin": {
82
- "hsm.d": "bin/hsm.d.ts",
83
78
  "hsm": "bin/hsm.js"
79
+ },
80
+ "engines": {
81
+ "node": "^18 || >=20"
84
82
  }
85
83
  }