hackmud-script-manager 0.19.1-4bde221 → 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,117 +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 = "Reduce character count further but lose function names in error call stacks"
32
- console.log(colourN("Version") + colourS(": ") + colourV("0.19.1-4bde221"))
33
- switch (commands[0]) {
34
- case "config":
35
- switch (commands[1]) {
36
- case "get":
37
- console.log(
38
- `\n${colourJ("Retrieve a value from the config file")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(`${commands[0]} ${commands[1]}`)} ${colourB("<key>")}`
39
- )
40
- break
41
- case "set":
42
- console.log(
43
- `\n${colourJ("Assign a value to the config file")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(`${commands[0]} ${commands[1]}`)} ${colourB("<key> <value>")}`
44
- )
45
- break
46
- case "delete":
47
- console.log(
48
- `\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>")}`
49
- )
50
- break
51
- default:
52
- console.log(
53
- colourS(
54
- `${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`
55
- )
56
- )
57
- }
58
- break
59
- case "push":
60
- console.log(
61
- colourS(
62
- `\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("--skip-minify")}\n Skip minification to produce a readable script\n${colourN("--mangle-names")}\n ${mangleNamesOptionDescription}\n${colourN("--force-quine-cheats")}\n Force quine cheats even if the character count is higher`
63
- )
64
- )
65
- break
66
- case "dev":
67
- case "watch":
68
- console.log(
69
- colourS(
70
- `${colourN("Aliases")}: ${colourV("watch, dev")}\n\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("--skip-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 Force quine cheats even if the character count is higher`
71
- )
72
- )
73
- break
74
- case "pull":
75
- console.log(
76
- colourS(
77
- `\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>")}`
78
- )
79
- )
80
- break
81
- case "minify":
82
- case "golf":
83
- console.log(
84
- colourS(
85
- `${colourN("Aliases")}: ${colourV("minify, golf")}\n\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("--skip-minify")}\n Skip minification to produce a readable script\n${colourN("--mangle-names")}\n ${mangleNamesOptionDescription}\n${colourN("--force-quine-cheats")}\n Force quine cheats even if the character count is higher\n${colourN("--watch")}\n Watch for changes`
86
- )
87
- )
88
- break
89
- case "generate-type-declaration":
90
- case "gen-type-declaration":
91
- case "gen-dts":
92
- case "gen-types":
93
- console.log(
94
- colourS(
95
- `${colourN("Aliases")}: ${colourV("generate-type-declaration, gen-type-declaration, gen-types, gen-dts")}\n\n${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]")}`
96
- )
97
- )
98
- break
99
- case "sync-macros":
100
- console.log("\n" + colourJ("Sync macros across all hackmud users"))
101
- break
102
- default:
103
- console.log(
104
- colourS(
105
- `\n${colourJ("Hackmud Script Manager")}\n\n${colourA("Commands:")}\n${colourL("push")}\n ${pushCommandDescription}\n${colourL("watch")}, ${colourL("dev")}\n Watch a directory and push a script when modified\n${colourL("minify")}, ${colourL("golf")}\n Minify a script file on the spot\n${colourL("generate-type-declaration")}, ${colourL("gen-type-declaration")}, ${colourL("gen-types")}, ${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`
106
- )
107
- )
108
- }
109
- },
110
- exploreObject = (object, keys, createPath = !1) => {
111
- for (const key of keys)
112
- object =
113
- createPath ?
114
- "object" == typeof object[key] ?
115
- object[key]
116
- : (object[key] = {})
117
- : object?.[key]
118
- return object
119
- },
120
- logInfo = ({ file, users, minLength, error }, hackmudPath) => {
121
- error ?
122
- logError(`error "${chalk.bold(error.message)}" in ${chalk.bold(file)}`)
123
- : console.log(
124
- `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")}`
125
- )
126
- },
127
- log = message => {
128
- console.log(colourS(message))
129
- },
130
- logError = message => {
131
- console.error(colourD(message))
132
- process.exitCode = 1
133
- }
28
+ log = message => console.log(colourS(message))
134
29
  for (const argument of process.argv.slice(2))
135
30
  if ("-" == argument[0]) {
136
31
  const [key, valueRaw] = argument.split("=")
@@ -147,20 +42,14 @@ for (const argument of process.argv.slice(2))
147
42
  else for (const option of key.slice(1)) options.set(option, value)
148
43
  } else commands.push(argument)
149
44
  if ("v" == commands[0] || "version" == commands[0] || options.get("version") || options.get("v")) {
150
- console.log("0.19.1-4bde221")
45
+ console.log("0.19.1-531ddb2")
151
46
  process.exit()
152
47
  }
153
48
  let configDidNotExist = !1
154
49
  const configPromise = readFile(configFilePath, { encoding: "utf-8" }).then(
155
50
  configFile => {
156
- let temporaryConfig
157
- try {
158
- temporaryConfig = JSON.parse(configFile)
159
- } catch {
160
- log("Config file was corrupted, resetting")
161
- return {}
162
- }
163
- if (!temporaryConfig || "object" != typeof temporaryConfig) {
51
+ const [temporaryConfig, error] = catchError(() => JSON.parse(configFile))
52
+ if (error || !isRecord(temporaryConfig)) {
164
53
  log("Config file was corrupted, resetting")
165
54
  return {}
166
55
  }
@@ -197,15 +86,13 @@ if (options.get("help") || options.get("h")) {
197
86
  process.exit()
198
87
  }
199
88
  let autoExit = !0
89
+ const getDefaultHackmudPath = () =>
90
+ "win32" == process.platform ? resolve(process.env.APPDATA, "hackmud") : resolve(homedir(), ".config/hackmud")
200
91
  switch (commands[0]) {
201
92
  case "push":
202
93
  {
203
- const { hackmudPath } = await configPromise
204
- if (!hackmudPath) {
205
- logNeedHackmudPathMessage()
206
- break
207
- }
208
- const sourcePath = commands[1]
94
+ const { hackmudPath = getDefaultHackmudPath() } = await configPromise,
95
+ sourcePath = commands[1]
209
96
  if (!sourcePath) {
210
97
  logError("Must provide the directory to push from\n")
211
98
  logHelp()
@@ -222,17 +109,20 @@ switch (commands[0]) {
222
109
  break
223
110
  }
224
111
  } else scripts.push("*.*")
225
- if (options.has("skip-minify") && options.has("mangle-names")) {
226
- logError(`Option ${colourN("--mangle-names")} is not compatible with ${colourN("--skip-minify")}\n`)
112
+ const optionsHasNoMinify = options.has("no-minify")
113
+ if ((optionsHasNoMinify || options.has("skip-minify")) && options.has("mangle-names")) {
114
+ logError(
115
+ `Options ${colourN("--mangle-names")} and ${colourN(optionsHasNoMinify ? "--no-minify" : "--skip-minify")} are incompatible\n`
116
+ )
227
117
  logHelp()
228
118
  break
229
119
  }
230
- const shouldSkipMinify = options.get("skip-minify")
120
+ const shouldSkipMinify = options.get("no-minify") || options.get("skip-minify")
231
121
  let shouldMinify
232
122
  if (null != shouldSkipMinify) {
233
123
  if ("boolean" != typeof shouldSkipMinify) {
234
124
  logError(
235
- `The value for ${colourN("--skip-minify")} must be ${colourV("true")} or ${colourV("false")}\n`
125
+ `The value for ${colourN(optionsHasNoMinify ? "--no-minify" : "--skip-minify")} must be ${colourV("true")} or ${colourV("false")}\n`
236
126
  )
237
127
  logHelp()
238
128
  break
@@ -270,12 +160,8 @@ switch (commands[0]) {
270
160
  case "dev":
271
161
  case "watch":
272
162
  {
273
- const { hackmudPath } = await configPromise
274
- if (!hackmudPath) {
275
- logNeedHackmudPathMessage()
276
- break
277
- }
278
- const sourcePath = commands[1]
163
+ const { hackmudPath = getDefaultHackmudPath() } = await configPromise,
164
+ sourcePath = commands[1]
279
165
  if (!sourcePath) {
280
166
  logError("Must provide the directory to watch\n")
281
167
  logHelp()
@@ -292,17 +178,20 @@ switch (commands[0]) {
292
178
  break
293
179
  }
294
180
  } else scripts.push("*.*")
295
- if (options.has("skip-minify") && options.has("mangle-names")) {
296
- logError(`Option ${colourN("--mangle-names")} is not compatible with ${colourN("--skip-minify")}\n`)
181
+ const optionsHasNoMinify = options.has("no-minify")
182
+ if ((optionsHasNoMinify || options.has("skip-minify")) && options.has("mangle-names")) {
183
+ logError(
184
+ `Options ${colourN("--mangle-names")} and ${colourN(optionsHasNoMinify ? "--no-minify" : "--skip-minify")} are incompatible\n`
185
+ )
297
186
  logHelp()
298
187
  break
299
188
  }
300
- const shouldSkipMinify = options.get("skip-minify")
189
+ const shouldSkipMinify = options.get("no-minify") || options.get("skip-minify")
301
190
  let shouldMinify
302
191
  if (null != shouldSkipMinify) {
303
192
  if ("boolean" != typeof shouldSkipMinify) {
304
193
  logError(
305
- `The value for ${colourN("--skip-minify")} must be ${colourV("true")} or ${colourV("false")}\n`
194
+ `The value for ${colourN(optionsHasNoMinify ? "--no-minify" : "--skip-minify")} must be ${colourV("true")} or ${colourV("false")}\n`
306
195
  )
307
196
  logHelp()
308
197
  break
@@ -345,34 +234,24 @@ switch (commands[0]) {
345
234
  break
346
235
  case "pull":
347
236
  {
348
- const { hackmudPath } = await configPromise
349
- if (!hackmudPath) {
350
- logNeedHackmudPathMessage()
351
- break
352
- }
353
- const script = commands[1]
237
+ const { hackmudPath = getDefaultHackmudPath() } = await configPromise,
238
+ script = commands[1]
354
239
  if (!script) {
355
240
  logError("Must provide the script to pull\n")
356
241
  logHelp()
357
242
  break
358
243
  }
359
244
  const sourcePath = commands[2] || "."
360
- try {
361
- await pull(sourcePath, hackmudPath, script)
362
- } catch (error) {
245
+ await pull(sourcePath, hackmudPath, script).catch(error => {
363
246
  console.error(error)
364
247
  logError(`Something went wrong, did you forget to ${colourC("#down")} the script?`)
365
- }
248
+ })
366
249
  }
367
250
  break
368
251
  case "sync-macros":
369
252
  {
370
- const { hackmudPath } = await configPromise
371
- if (!hackmudPath) {
372
- logNeedHackmudPathMessage()
373
- break
374
- }
375
- const { macrosSynced, usersSynced } = await syncMacros(hackmudPath)
253
+ const { hackmudPath = getDefaultHackmudPath() } = await configPromise,
254
+ { macrosSynced, usersSynced } = await syncMacros(hackmudPath)
376
255
  log(`Synced ${macrosSynced} macros to ${usersSynced} users`)
377
256
  }
378
257
  break
@@ -389,16 +268,17 @@ switch (commands[0]) {
389
268
  }
390
269
  const sourcePath = resolve(target),
391
270
  outputPath = commands[2] || "./player.d.ts",
392
- typeDeclaration = await generateTypeDeclaration(sourcePath, (await configPromise).hackmudPath)
271
+ typeDeclaration = await generateTypeDeclaration(
272
+ sourcePath,
273
+ (await configPromise).hackmudPath || getDefaultHackmudPath()
274
+ )
393
275
  let typeDeclarationPath = resolve(outputPath)
394
- try {
395
- await writeFile(typeDeclarationPath, typeDeclaration)
396
- } catch (error) {
397
- assert(error instanceof Error)
276
+ await writeFile(typeDeclarationPath, typeDeclaration).catch(error => {
277
+ assert(error instanceof Error, "src/bin/hsm.ts:365:35")
398
278
  if ("EISDIR" != error.code) throw error
399
279
  typeDeclarationPath = resolve(typeDeclarationPath, "player.d.ts")
400
- await writeFile(typeDeclarationPath, typeDeclaration)
401
- }
280
+ return writeFile(typeDeclarationPath, typeDeclaration)
281
+ })
402
282
  log("Wrote type declaration to " + chalk.bold(typeDeclarationPath))
403
283
  }
404
284
  break
@@ -406,8 +286,14 @@ switch (commands[0]) {
406
286
  switch (commands[1]) {
407
287
  case "get":
408
288
  {
409
- const key = commands[2]
410
- 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)
411
297
  }
412
298
  break
413
299
  case "delete":
@@ -418,14 +304,16 @@ switch (commands[0]) {
418
304
  logHelp()
419
305
  break
420
306
  }
421
- const keyParts = key.split("."),
422
- pathName = keyParts
423
- .map(name => (/^[a-z_$][\w$]*$/i.test(name) ? name : JSON.stringify(name)))
424
- .join("."),
425
- lastKey = keyParts.pop(),
426
- config = await configPromise
427
- delete exploreObject(config, keyParts)?.[lastKey]
428
- 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))
429
317
  }
430
318
  break
431
319
  case "set":
@@ -437,49 +325,20 @@ switch (commands[0]) {
437
325
  logHelp()
438
326
  break
439
327
  }
440
- const keys = key.split("."),
441
- pathName = keys
442
- .map(name => (/^[a-z_$][\w$]*$/i.test(name) ? name : JSON.stringify(name)))
443
- .join(".")
444
328
  if (!value) {
445
- logError(`Must provide a value for the key ${pathName}\n`)
329
+ logError(`Must provide a value for the key ${colourV(key)}\n`)
446
330
  logHelp()
447
331
  break
448
332
  }
449
- const lastKey = keys.pop(),
450
- config = await configPromise
451
- if (keys.length || "hackmudPath" != lastKey) {
452
- let object = config
453
- for (const key of keys)
454
- if ("object" == typeof object[key]) object = object[key]
455
- else {
456
- object[key] = {}
457
- object = object[key]
458
- }
459
- object[lastKey] = value
460
- } 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)}:`)
461
336
  console.log(config)
462
- await (async config => {
463
- const json = JSON.stringify(config, void 0, "\t")
464
- configDidNotExist && log("Creating config file at " + configFilePath)
465
- await writeFile(configFilePath, json).catch(async error => {
466
- switch (error.code) {
467
- case "EISDIR":
468
- await rmdir(configFilePath)
469
- break
470
- case "ENOENT":
471
- await mkdir(configDirectoryPath)
472
- break
473
- default:
474
- throw error
475
- }
476
- await writeFile(configFilePath, json)
477
- })
478
- })(config)
337
+ await updateConfig(config)
479
338
  }
480
339
  break
481
340
  default:
482
- commands[1] && logError(`Unknown command: ${JSON.stringify(commands[1])}\n`)
341
+ commands[1] && logError(`Unknown command: ${colourL(commands[1])}\n`)
483
342
  logHelp()
484
343
  }
485
344
  break
@@ -511,9 +370,11 @@ switch (commands[0]) {
511
370
  "scripts" == basename(resolve(target, "..")) && "hackmud" == basename(resolve(target, "../../..")) ?
512
371
  basename(resolve(target, "../.."))
513
372
  : "UNKNOWN",
514
- minify = !options.get("skip-minify")
515
- if (options.has("skip-minify") && options.has("mangle-names")) {
516
- logError(`Option ${colourN("--mangle-names")} would have no effect if minification is skipped\n`)
373
+ optionsHasNoMinify = options.has("no-minify")
374
+ if ((optionsHasNoMinify || options.has("skip-minify")) && options.has("mangle-names")) {
375
+ logError(
376
+ `Options ${colourN("--mangle-names")} and ${colourN(optionsHasNoMinify ? "--no-minify" : "--skip-minify")} are incompatible\n`
377
+ )
517
378
  logHelp()
518
379
  break
519
380
  }
@@ -544,36 +405,31 @@ switch (commands[0]) {
544
405
  : fileBaseName + ".js"
545
406
  )
546
407
  const golfFile = () =>
547
- readFile(target, { encoding: "utf-8" }).then(
548
- async source => {
549
- const timeStart = performance.now(),
550
- { script, warnings } = await processScript(source, {
551
- minify,
552
- scriptUser,
553
- scriptName,
554
- filePath: target,
555
- mangleNames,
556
- forceQuineCheats
557
- }),
558
- timeTook = performance.now() - timeStart
559
- for (const { message, line } of warnings)
560
- log(`Warning "${chalk.bold(message)}" on line ${chalk.bold(line + "")}`)
561
- await writeFilePersistent(outputPath, script)
562
- .catch(async error => {
563
- if (!commands[2] || "EISDIR" != error.code) throw error
564
- outputPath = resolve(outputPath, basename(target, fileExtension) + ".js")
565
- await writeFilePersistent(outputPath, script)
566
- })
567
- .then(
568
- () =>
569
- log(
570
- `Wrote ${chalk.bold(countHackmudCharacters(script))} chars to ${chalk.bold(relative(".", outputPath))} | took ${Math.round(100 * timeTook) / 100}ms`
571
- ),
572
- 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`
573
430
  )
574
- },
575
- error => logError(error.message)
576
- )
431
+ )
432
+ })
577
433
  if (options.get("watch")) {
578
434
  const { watch: watchFile } = await chokidarModule
579
435
  watchFile(target, { awaitWriteFinish: { stabilityThreshold: 100 } })
@@ -584,7 +440,110 @@ switch (commands[0]) {
584
440
  }
585
441
  break
586
442
  default:
587
- commands[0] && logError(`Unknown command: ${JSON.stringify(commands[0])}\n`)
443
+ commands[0] && logError(`Unknown command: ${colourL(commands[0])}\n`)
588
444
  logHelp()
589
445
  }
590
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
  )