hackmud-script-manager 0.19.1-27bbd7d → 0.19.1-29fcf72

Sign up to get free protection for your applications and to get access to all the features.
package/README.md CHANGED
@@ -1,6 +1,4 @@
1
1
  # Hackmud Script Manager
2
- ![Test](https://github.com/samualtnorman/hackmud-script-manager/workflows/Test/badge.svg)
3
-
4
2
  Command made for [hackmud-environment](https://github.com/samualtnorman/hackmud-environment), which is a scripting environment for hackmud with minification, autocompletes / intellisense, and TypeScript support.
5
3
 
6
4
  Install with `npm install hackmud-script-manager -g` to make the `hsm` command available everywhere.
package/bin/hsm.js CHANGED
@@ -3,16 +3,16 @@ import { Cache } from "@samual/lib/Cache"
3
3
  import { assert } from "@samual/lib/assert"
4
4
  import { countHackmudCharacters } from "@samual/lib/countHackmudCharacters"
5
5
  import { writeFilePersistent } from "@samual/lib/writeFilePersistent"
6
- import { readFile, writeFile, mkdir, rmdir } from "fs/promises"
6
+ import { writeFile, readFile } from "fs/promises"
7
7
  import { homedir } from "os"
8
- import { resolve, extname, basename, dirname, relative } from "path"
8
+ import { extname, basename, resolve, dirname, relative } from "path"
9
9
  import { supportedExtensions } from "../constants.js"
10
10
  import { generateTypeDeclaration } from "../generateTypeDeclaration.js"
11
11
  import { pull } from "../pull.js"
12
12
  import { syncMacros } from "../syncMacros.js"
13
+ import "@samual/lib/readDirectoryWithStats"
13
14
  import "@samual/lib/copyFilePersistent"
14
- const configDirectoryPath = resolve(homedir(), ".config"),
15
- configFilePath = resolve(configDirectoryPath, "hsm.json"),
15
+ const version = "0.19.1-29fcf72",
16
16
  options = new Map(),
17
17
  commands = [],
18
18
  userColours = new Cache(user => {
@@ -20,117 +20,7 @@ const configDirectoryPath = resolve(homedir(), ".config"),
20
20
  for (const char of user) hash += (hash >> 1) + hash + "xi1_8ratvsw9hlbgm02y5zpdcn7uekof463qj".indexOf(char) + 1
21
21
  return [colourJ, colourK, colourM, colourW, colourL, colourB][hash % 6](user)
22
22
  }),
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-27bbd7d"))
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
- }
23
+ log = message => console.log(colourS(message))
134
24
  for (const argument of process.argv.slice(2))
135
25
  if ("-" == argument[0]) {
136
26
  const [key, valueRaw] = argument.split("=")
@@ -147,35 +37,10 @@ for (const argument of process.argv.slice(2))
147
37
  else for (const option of key.slice(1)) options.set(option, value)
148
38
  } else commands.push(argument)
149
39
  if ("v" == commands[0] || "version" == commands[0] || options.get("version") || options.get("v")) {
150
- console.log("0.19.1-27bbd7d")
40
+ console.log(version)
151
41
  process.exit()
152
42
  }
153
- let configDidNotExist = !1
154
- const configPromise = readFile(configFilePath, { encoding: "utf-8" }).then(
155
- 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) {
164
- log("Config file was corrupted, resetting")
165
- return {}
166
- }
167
- if ("hackmudPath" in temporaryConfig && "string" != typeof temporaryConfig.hackmudPath) {
168
- log('Property "hackmudPath" of config file was corrupted, removing')
169
- delete temporaryConfig.hackmudPath
170
- }
171
- return temporaryConfig
172
- },
173
- () => {
174
- configDidNotExist = !0
175
- return {}
176
- }
177
- ),
178
- pushModule = import("../push.js"),
43
+ const pushModule = import("../push.js"),
179
44
  processScriptModule = import("../processScript/index.js"),
180
45
  watchModule = import("../watch.js"),
181
46
  chokidarModule = import("chokidar"),
@@ -200,12 +65,8 @@ let autoExit = !0
200
65
  switch (commands[0]) {
201
66
  case "push":
202
67
  {
203
- const { hackmudPath } = await configPromise
204
- if (!hackmudPath) {
205
- logNeedHackmudPathMessage()
206
- break
207
- }
208
- const sourcePath = commands[1]
68
+ const hackmudPath = getHackmudPath(),
69
+ sourcePath = commands[1]
209
70
  if (!sourcePath) {
210
71
  logError("Must provide the directory to push from\n")
211
72
  logHelp()
@@ -222,17 +83,20 @@ switch (commands[0]) {
222
83
  break
223
84
  }
224
85
  } 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`)
86
+ const optionsHasNoMinify = options.has("no-minify")
87
+ if ((optionsHasNoMinify || options.has("skip-minify")) && options.has("mangle-names")) {
88
+ logError(
89
+ `Options ${colourN("--mangle-names")} and ${colourN(optionsHasNoMinify ? "--no-minify" : "--skip-minify")} are incompatible\n`
90
+ )
227
91
  logHelp()
228
92
  break
229
93
  }
230
- const shouldSkipMinify = options.get("skip-minify")
94
+ const shouldSkipMinify = options.get("no-minify") || options.get("skip-minify")
231
95
  let shouldMinify
232
96
  if (null != shouldSkipMinify) {
233
97
  if ("boolean" != typeof shouldSkipMinify) {
234
98
  logError(
235
- `The value for ${colourN("--skip-minify")} must be ${colourV("true")} or ${colourV("false")}\n`
99
+ `The value for ${colourN(optionsHasNoMinify ? "--no-minify" : "--skip-minify")} must be ${colourV("true")} or ${colourV("false")}\n`
236
100
  )
237
101
  logHelp()
238
102
  break
@@ -270,12 +134,8 @@ switch (commands[0]) {
270
134
  case "dev":
271
135
  case "watch":
272
136
  {
273
- const { hackmudPath } = await configPromise
274
- if (!hackmudPath) {
275
- logNeedHackmudPathMessage()
276
- break
277
- }
278
- const sourcePath = commands[1]
137
+ const hackmudPath = getHackmudPath(),
138
+ sourcePath = commands[1]
279
139
  if (!sourcePath) {
280
140
  logError("Must provide the directory to watch\n")
281
141
  logHelp()
@@ -292,17 +152,20 @@ switch (commands[0]) {
292
152
  break
293
153
  }
294
154
  } 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`)
155
+ const optionsHasNoMinify = options.has("no-minify")
156
+ if ((optionsHasNoMinify || options.has("skip-minify")) && options.has("mangle-names")) {
157
+ logError(
158
+ `Options ${colourN("--mangle-names")} and ${colourN(optionsHasNoMinify ? "--no-minify" : "--skip-minify")} are incompatible\n`
159
+ )
297
160
  logHelp()
298
161
  break
299
162
  }
300
- const shouldSkipMinify = options.get("skip-minify")
163
+ const shouldSkipMinify = options.get("no-minify") || options.get("skip-minify")
301
164
  let shouldMinify
302
165
  if (null != shouldSkipMinify) {
303
166
  if ("boolean" != typeof shouldSkipMinify) {
304
167
  logError(
305
- `The value for ${colourN("--skip-minify")} must be ${colourV("true")} or ${colourV("false")}\n`
168
+ `The value for ${colourN(optionsHasNoMinify ? "--no-minify" : "--skip-minify")} must be ${colourV("true")} or ${colourV("false")}\n`
306
169
  )
307
170
  logHelp()
308
171
  break
@@ -345,34 +208,24 @@ switch (commands[0]) {
345
208
  break
346
209
  case "pull":
347
210
  {
348
- const { hackmudPath } = await configPromise
349
- if (!hackmudPath) {
350
- logNeedHackmudPathMessage()
351
- break
352
- }
353
- const script = commands[1]
211
+ const hackmudPath = getHackmudPath(),
212
+ script = commands[1]
354
213
  if (!script) {
355
214
  logError("Must provide the script to pull\n")
356
215
  logHelp()
357
216
  break
358
217
  }
359
218
  const sourcePath = commands[2] || "."
360
- try {
361
- await pull(sourcePath, hackmudPath, script)
362
- } catch (error) {
219
+ await pull(sourcePath, hackmudPath, script).catch(error => {
363
220
  console.error(error)
364
221
  logError(`Something went wrong, did you forget to ${colourC("#down")} the script?`)
365
- }
222
+ })
366
223
  }
367
224
  break
368
225
  case "sync-macros":
369
226
  {
370
- const { hackmudPath } = await configPromise
371
- if (!hackmudPath) {
372
- logNeedHackmudPathMessage()
373
- break
374
- }
375
- const { macrosSynced, usersSynced } = await syncMacros(hackmudPath)
227
+ const hackmudPath = getHackmudPath(),
228
+ { macrosSynced, usersSynced } = await syncMacros(hackmudPath)
376
229
  log(`Synced ${macrosSynced} macros to ${usersSynced} users`)
377
230
  }
378
231
  break
@@ -389,100 +242,17 @@ switch (commands[0]) {
389
242
  }
390
243
  const sourcePath = resolve(target),
391
244
  outputPath = commands[2] || "./player.d.ts",
392
- typeDeclaration = await generateTypeDeclaration(sourcePath, (await configPromise).hackmudPath)
245
+ typeDeclaration = await generateTypeDeclaration(sourcePath, getHackmudPath())
393
246
  let typeDeclarationPath = resolve(outputPath)
394
- try {
395
- await writeFile(typeDeclarationPath, typeDeclaration)
396
- } catch (error) {
397
- assert(error instanceof Error)
247
+ await writeFile(typeDeclarationPath, typeDeclaration).catch(error => {
248
+ assert(error instanceof Error, "src/bin/hsm.ts:327:35")
398
249
  if ("EISDIR" != error.code) throw error
399
250
  typeDeclarationPath = resolve(typeDeclarationPath, "player.d.ts")
400
- await writeFile(typeDeclarationPath, typeDeclaration)
401
- }
251
+ return writeFile(typeDeclarationPath, typeDeclaration)
252
+ })
402
253
  log("Wrote type declaration to " + chalk.bold(typeDeclarationPath))
403
254
  }
404
255
  break
405
- case "config":
406
- switch (commands[1]) {
407
- case "get":
408
- {
409
- const key = commands[2]
410
- key ? log(exploreObject(await configPromise, key.split("."))) : console.log(await configPromise)
411
- }
412
- break
413
- case "delete":
414
- {
415
- const key = commands[2]
416
- if (!key) {
417
- logError("Must provide a key to delete\n")
418
- logHelp()
419
- break
420
- }
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`)
429
- }
430
- break
431
- case "set":
432
- {
433
- const key = commands[2],
434
- value = commands[3]
435
- if (!key) {
436
- logError("Must provide a key and value\n")
437
- logHelp()
438
- break
439
- }
440
- const keys = key.split("."),
441
- pathName = keys
442
- .map(name => (/^[a-z_$][\w$]*$/i.test(name) ? name : JSON.stringify(name)))
443
- .join(".")
444
- if (!value) {
445
- logError(`Must provide a value for the key ${pathName}\n`)
446
- logHelp()
447
- break
448
- }
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)
461
- 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)
479
- }
480
- break
481
- default:
482
- commands[1] && logError(`Unknown command: ${JSON.stringify(commands[1])}\n`)
483
- logHelp()
484
- }
485
- break
486
256
  case "help":
487
257
  case "h":
488
258
  logHelp()
@@ -511,9 +281,11 @@ switch (commands[0]) {
511
281
  "scripts" == basename(resolve(target, "..")) && "hackmud" == basename(resolve(target, "../../..")) ?
512
282
  basename(resolve(target, "../.."))
513
283
  : "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`)
284
+ optionsHasNoMinify = options.has("no-minify")
285
+ if ((optionsHasNoMinify || options.has("skip-minify")) && options.has("mangle-names")) {
286
+ logError(
287
+ `Options ${colourN("--mangle-names")} and ${colourN(optionsHasNoMinify ? "--no-minify" : "--skip-minify")} are incompatible\n`
288
+ )
517
289
  logHelp()
518
290
  break
519
291
  }
@@ -544,36 +316,31 @@ switch (commands[0]) {
544
316
  : fileBaseName + ".js"
545
317
  )
546
318
  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)
319
+ readFile(target, { encoding: "utf-8" }).then(async source => {
320
+ const timeStart = performance.now(),
321
+ { script, warnings } = await processScript(source, {
322
+ minify: !(options.get("no-minify") || options.get("skip-minify")),
323
+ scriptUser,
324
+ scriptName,
325
+ filePath: target,
326
+ mangleNames,
327
+ forceQuineCheats
328
+ }),
329
+ timeTook = performance.now() - timeStart
330
+ for (const { message, line } of warnings)
331
+ log(`Warning "${chalk.bold(message)}" on line ${chalk.bold(line + "")}`)
332
+ await writeFilePersistent(outputPath, script)
333
+ .catch(error => {
334
+ if (!commands[2] || "EISDIR" != error.code) throw error
335
+ outputPath = resolve(outputPath, basename(target, fileExtension) + ".js")
336
+ return writeFilePersistent(outputPath, script)
337
+ })
338
+ .then(() =>
339
+ log(
340
+ `Wrote ${chalk.bold(countHackmudCharacters(script))} chars to ${chalk.bold(relative(".", outputPath))} | took ${Math.round(100 * timeTook) / 100}ms`
573
341
  )
574
- },
575
- error => logError(error.message)
576
- )
342
+ )
343
+ })
577
344
  if (options.get("watch")) {
578
345
  const { watch: watchFile } = await chokidarModule
579
346
  watchFile(target, { awaitWriteFinish: { stabilityThreshold: 100 } })
@@ -584,7 +351,86 @@ switch (commands[0]) {
584
351
  }
585
352
  break
586
353
  default:
587
- commands[0] && logError(`Unknown command: ${JSON.stringify(commands[0])}\n`)
354
+ commands[0] && logError(`Unknown command: ${colourL(commands[0])}\n`)
588
355
  logHelp()
589
356
  }
590
357
  autoExit && process.exit()
358
+ function logHelp() {
359
+ const pushCommandDescription = "Push scripts from a directory to hackmud user's scripts directories",
360
+ forceQuineCheatsOptionDescription = `Force quine cheats on. Use ${colourN("--force-quine-cheats")}=${colourV("false")} to force off`,
361
+ hackmudPathOption = `${colourN("--hackmud-path")}=${colourB("<path>")}\n Override hackmud path`
362
+ console.log(colourN("Version") + colourS(": ") + colourV(version))
363
+ switch (commands[0]) {
364
+ case "dev":
365
+ case "watch":
366
+ case "push":
367
+ console.log(
368
+ colourS(
369
+ `\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${hackmudPathOption}\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`
370
+ )
371
+ )
372
+ break
373
+ case "pull":
374
+ console.log(
375
+ colourS(
376
+ `\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>")}\n\n${colourA("Options:")}\n${hackmudPathOption}`
377
+ )
378
+ )
379
+ break
380
+ case "minify":
381
+ case "golf":
382
+ console.log(
383
+ colourS(
384
+ `\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`
385
+ )
386
+ )
387
+ break
388
+ case "generate-type-declaration":
389
+ case "gen-type-declaration":
390
+ case "gen-dts":
391
+ case "gen-types":
392
+ console.log(
393
+ colourS(
394
+ `${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]")}\n\n${colourA("Options:")}\n${hackmudPathOption}`
395
+ )
396
+ )
397
+ break
398
+ case "sync-macros":
399
+ console.log(
400
+ colourS(
401
+ `\n${colourJ("Sync macros across all hackmud users")}\n\n${colourA("Options:")}\n${hackmudPathOption}`
402
+ )
403
+ )
404
+ break
405
+ default:
406
+ console.log(
407
+ colourS(
408
+ `\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("pull")}\n Pull a script a from a hackmud user's script directory`
409
+ )
410
+ )
411
+ }
412
+ }
413
+ function logInfo({ file, users, minLength, error }, hackmudPath) {
414
+ error ?
415
+ logError(`error "${chalk.bold(error.message)}" in ${chalk.bold(file)}`)
416
+ : console.log(
417
+ `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")}`
418
+ )
419
+ }
420
+ function logError(message) {
421
+ console.error(colourD(message))
422
+ process.exitCode = 1
423
+ }
424
+ function getHackmudPath() {
425
+ const hackmudPathOption = options.get("hackmud-path")
426
+ if (null != hackmudPathOption && "string" != typeof hackmudPathOption) {
427
+ logError(`Option ${colourN("--hackmud-path")} must be a string, got ${colourV(hackmudPathOption)}\n`)
428
+ logHelp()
429
+ process.exit(1)
430
+ }
431
+ return (
432
+ hackmudPathOption ||
433
+ process.env.HSM_HACKMUD_PATH ||
434
+ ("win32" == process.platform ? resolve(process.env.APPDATA, "hackmud") : resolve(homedir(), ".config/hackmud"))
435
+ )
436
+ }
@@ -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"