hackmud-script-manager 0.20.4-67aeb81 → 0.20.4-6815ce0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -20,6 +20,10 @@ You can read about how HSM works [in my blog post](https://samual.uk/blog/js-cod
20
20
  > ```
21
21
  > You will need to run `Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope CurrentUser` in PowerShell as an administrator. For more information, see [Microsoft's page about Execution Policies](https://learn.microsoft.com/en-gb/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-7.4).
22
22
 
23
+ ![image](https://github.com/samualtnorman/hackmud-script-manager/assets/18307063/69a371fe-f8c8-43fe-b3c7-39f3735ce6fb)
24
+ ![image](https://github.com/samualtnorman/hackmud-script-manager/assets/18307063/08103f9e-74fa-4a56-a739-94858ba8c139)
25
+ ![image](https://github.com/samualtnorman/hackmud-script-manager/assets/18307063/25ccb86d-1fe3-4632-b703-ac47f5b32c9c)
26
+
23
27
  ## Features
24
28
  - Minification
25
29
  - This includes auto quine cheating.
package/bin/hsm.js CHANGED
@@ -1,21 +1,22 @@
1
1
  #!/usr/bin/env node
2
- import { Cache } from "@samual/lib/Cache"
2
+ import { AutoMap } from "@samual/lib/AutoMap"
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
6
  import { writeFile, readFile } from "fs/promises"
7
7
  import { homedir } from "os"
8
- import { extname, basename, resolve, dirname, relative } from "path"
8
+ import { resolve, extname, basename, 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
13
  import "@samual/lib/readDirectoryWithStats"
14
+ import "path/posix"
14
15
  import "@samual/lib/copyFilePersistent"
15
- const version = "0.20.4-67aeb81",
16
+ const formatOption = name => colourN(`-${1 == name.length ? "" : "-"}${name}`),
16
17
  options = new Map(),
17
18
  commands = [],
18
- userColours = new Cache(user => {
19
+ userColours = new AutoMap(user => {
19
20
  let hash = 0
20
21
  for (const char of user) hash += (hash >> 1) + hash + "xi1_8ratvsw9hlbgm02y5zpdcn7uekof463qj".indexOf(char) + 1
21
22
  return [colourJ, colourK, colourM, colourW, colourL, colourB][hash % 6](user)
@@ -36,10 +37,6 @@ for (const argument of process.argv.slice(2))
36
37
  if ("-" == argument[1]) options.set(key.slice(2), value)
37
38
  else for (const option of key.slice(1)) options.set(option, value)
38
39
  } else commands.push(argument)
39
- if ("v" == commands[0] || "version" == commands[0] || options.get("version") || options.get("v")) {
40
- console.log(version)
41
- process.exit()
42
- }
43
40
  const pushModule = import("../push.js"),
44
41
  processScriptModule = import("../processScript/index.js"),
45
42
  watchModule = import("../watch.js"),
@@ -49,6 +46,7 @@ const pushModule = import("../push.js"),
49
46
  colourB = chalk.rgb(202, 202, 202),
50
47
  colourC = chalk.rgb(155, 155, 155),
51
48
  colourD = chalk.rgb(255, 0, 0),
49
+ colourF = chalk.rgb(255, 128, 0),
52
50
  colourJ = chalk.rgb(255, 244, 4),
53
51
  colourK = chalk.rgb(243, 249, 152),
54
52
  colourL = chalk.rgb(30, 255, 0),
@@ -57,162 +55,171 @@ const pushModule = import("../push.js"),
57
55
  colourS = chalk.rgb(122, 178, 244),
58
56
  colourV = chalk.rgb(255, 0, 236),
59
57
  colourW = chalk.rgb(255, 150, 224)
60
- if (options.get("help") || options.get("h")) {
58
+ process.version.startsWith("v21.") &&
59
+ console.warn(
60
+ colourF(
61
+ `Warning: Support for Node.js 21 will be dropped in the next minor version of HSM\n Your current version of Node.js is ${chalk.bold(process.version)}\n You should update your version of Node.js\n https://nodejs.org/en/download/package-manager\n`
62
+ )
63
+ )
64
+ if ("v" == commands[0] || "version" == commands[0] || popOption("version", "v")?.value) {
65
+ console.log("0.20.4-6815ce0")
66
+ process.exit()
67
+ }
68
+ if (popOption("help", "h")?.value) {
61
69
  logHelp()
62
70
  process.exit()
63
71
  }
64
72
  let autoExit = !0
65
73
  switch (commands[0]) {
66
74
  case "push":
75
+ case "dev":
76
+ case "watch":
77
+ case "golf":
78
+ case "minify":
67
79
  {
68
- const hackmudPath = getHackmudPath(),
69
- sourcePath = commands[1]
70
- if (!sourcePath) {
71
- logError("Must provide the directory to push from\n")
72
- logHelp()
73
- break
74
- }
75
- const scripts = commands.slice(2)
76
- if (scripts.length) {
77
- const invalidScript = scripts.find(
78
- script => !/^(?:[a-z_][a-z\d_]{0,24}|\*)\.(?:[a-z_][a-z\d_]{0,24}|\*)$/.test(script)
79
- )
80
- if (invalidScript) {
81
- logError(`Invalid script name: ${JSON.stringify(invalidScript)}\n`)
82
- logHelp()
83
- break
84
- }
85
- } else scripts.push("*.*")
86
- const optionsHasNoMinify = options.has("no-minify")
87
- if ((optionsHasNoMinify || options.has("skip-minify")) && options.has("mangle-names")) {
80
+ const noMinifyOption = popOption("no-minify", "skip-minify"),
81
+ mangleNamesOption = popOption("mangle-names"),
82
+ forceQuineCheatsOption = popOption("force-quine-cheats"),
83
+ noMinifyIncompatibleOption = mangleNamesOption || forceQuineCheatsOption
84
+ if (noMinifyOption && noMinifyIncompatibleOption) {
88
85
  logError(
89
- `Options ${colourN("--mangle-names")} and ${colourN(optionsHasNoMinify ? "--no-minify" : "--skip-minify")} are incompatible\n`
86
+ `Options ${formatOption(noMinifyOption.name)} and ${formatOption(noMinifyIncompatibleOption.name)} are incompatible\n`
90
87
  )
91
88
  logHelp()
92
- break
89
+ process.exit(1)
93
90
  }
94
- const shouldSkipMinify = options.get("no-minify") || options.get("skip-minify")
95
- let shouldMinify
96
- if (null != shouldSkipMinify) {
97
- if ("boolean" != typeof shouldSkipMinify) {
91
+ noMinifyOption && assertOptionIsBoolean(noMinifyOption)
92
+ mangleNamesOption && assertOptionIsBoolean(mangleNamesOption)
93
+ forceQuineCheatsOption && assertOptionIsBoolean(forceQuineCheatsOption)
94
+ if ("golf" == commands[0] || "minify" == commands[0]) {
95
+ const watchOption = popOption("watch"),
96
+ target = commands[1]
97
+ if (!target) {
98
+ logError("Must provide target\n")
99
+ logHelp()
100
+ process.exit(1)
101
+ }
102
+ const fileExtension = extname(target)
103
+ if (!supportedExtensions.includes(fileExtension)) {
98
104
  logError(
99
- `The value for ${colourN(optionsHasNoMinify ? "--no-minify" : "--skip-minify")} must be ${colourV("true")} or ${colourV("false")}\n`
105
+ `Unsupported file extension "${chalk.bold(fileExtension)}"\nSupported extensions are "${supportedExtensions.map(extension => chalk.bold(extension)).join('", "')}"`
100
106
  )
101
- logHelp()
102
- break
107
+ process.exit(1)
103
108
  }
104
- shouldMinify = !shouldSkipMinify
105
- }
106
- const shouldMangleNames = options.get("mangle-names")
107
- if (null != shouldMangleNames && "boolean" != typeof shouldMangleNames) {
108
- logError(
109
- `The value for ${colourN("--mangle-names")} must be ${colourV("true")} or ${colourV("false")}\n`
110
- )
111
- logHelp()
112
- break
113
- }
114
- const shouldforceQuineCheats = options.get("force-quine-cheats")
115
- if (null != shouldforceQuineCheats && "boolean" != typeof shouldforceQuineCheats) {
116
- logError(
117
- `The value for ${colourN("--force-quine-cheats")} must be ${colourV("true")} or ${colourV("false")}\n`
118
- )
119
- logHelp()
120
- break
121
- }
122
- const { push, MissingSourceFolderError, MissingHackmudFolderError, NoUsersError } = await pushModule,
123
- infos = await push(sourcePath, hackmudPath, {
124
- scripts,
125
- onPush: info => logInfo(info, hackmudPath),
126
- minify: shouldMinify,
127
- mangleNames: shouldMangleNames,
128
- forceQuineCheats: shouldforceQuineCheats
129
- })
130
- if (infos instanceof Error) {
131
- logError(infos.message)
132
- if (infos instanceof MissingSourceFolderError || infos instanceof NoUsersError) {
133
- console.log()
134
- logHelp()
135
- } else
136
- infos instanceof MissingHackmudFolderError &&
137
- log(
138
- `If this is not where your hackmud folder is, you can specify it with the\n${colourN("--hackmud-path")}=${colourB("<path>")} option or ${colourN("HSM_HACKMUD_PATH")} environment variable`
139
- )
140
- } else infos.length || logError("Could not find any scripts to push")
141
- }
142
- break
143
- case "dev":
144
- case "watch":
145
- {
146
- const hackmudPath = getHackmudPath(),
147
- sourcePath = commands[1]
148
- if (!sourcePath) {
149
- logError("Must provide the directory to watch\n")
150
- logHelp()
151
- break
152
- }
153
- const scripts = commands.slice(2)
154
- if (scripts.length) {
155
- const invalidScript = scripts.find(
156
- script => !/^(?:[a-z_][a-z\d_]{0,24}|\*)\.(?:[a-z_][a-z\d_]{0,24}|\*)$/.test(script)
157
- )
158
- if (invalidScript) {
159
- logError(`Invalid script name: ${JSON.stringify(invalidScript)}\n`)
109
+ complainAboutUnrecognisedOptions()
110
+ const { processScript } = await processScriptModule,
111
+ fileBaseName = basename(target, fileExtension),
112
+ fileBaseNameEndsWithDotSrc = fileBaseName.endsWith(".src"),
113
+ scriptName = fileBaseNameEndsWithDotSrc ? fileBaseName.slice(0, -4) : fileBaseName,
114
+ scriptUser =
115
+ (
116
+ "scripts" == basename(resolve(target, "..")) &&
117
+ "hackmud" == basename(resolve(target, "../../.."))
118
+ ) ?
119
+ basename(resolve(target, "../.."))
120
+ : void 0
121
+ let outputPath =
122
+ commands[2] ||
123
+ resolve(
124
+ dirname(target),
125
+ fileBaseNameEndsWithDotSrc ? scriptName + ".js"
126
+ : ".js" == fileExtension ? fileBaseName + ".min.js"
127
+ : fileBaseName + ".js"
128
+ )
129
+ const golfFile = () =>
130
+ readFile(target, { encoding: "utf8" }).then(async source => {
131
+ const timeStart = performance.now(),
132
+ { script, warnings } = await processScript(source, {
133
+ minify: noMinifyOption && !noMinifyOption.value,
134
+ scriptUser,
135
+ scriptName,
136
+ filePath: target,
137
+ mangleNames: mangleNamesOption?.value,
138
+ forceQuineCheats: forceQuineCheatsOption?.value
139
+ }),
140
+ timeTook = performance.now() - timeStart
141
+ for (const { message, line } of warnings)
142
+ log(`Warning "${chalk.bold(message)}" on line ${chalk.bold(line + "")}`)
143
+ await writeFilePersistent(outputPath, script)
144
+ .catch(error => {
145
+ if (!commands[2] || "EISDIR" != error.code) throw error
146
+ outputPath = resolve(outputPath, basename(target, fileExtension) + ".js")
147
+ return writeFilePersistent(outputPath, script)
148
+ })
149
+ .then(() =>
150
+ log(
151
+ `Wrote ${chalk.bold(countHackmudCharacters(script))} chars to ${chalk.bold(relative(".", outputPath))} | took ${Math.round(100 * timeTook) / 100}ms`
152
+ )
153
+ )
154
+ })
155
+ if (watchOption) {
156
+ const { watch: watchFile } = await chokidarModule
157
+ watchFile(target, { awaitWriteFinish: { stabilityThreshold: 100 } })
158
+ .on("ready", () => log("Watching " + target))
159
+ .on("change", golfFile)
160
+ autoExit = !1
161
+ } else await golfFile()
162
+ } else {
163
+ const hackmudPath = getHackmudPath(),
164
+ sourcePath = commands[1]
165
+ if (!sourcePath) {
166
+ logError(`Must provide the directory to ${"push" == commands[0] ? "push from" : "watch"}\n`)
160
167
  logHelp()
161
- break
168
+ process.exit(1)
162
169
  }
163
- } else scripts.push("*.*")
164
- const optionsHasNoMinify = options.has("no-minify")
165
- if ((optionsHasNoMinify || options.has("skip-minify")) && options.has("mangle-names")) {
166
- logError(
167
- `Options ${colourN("--mangle-names")} and ${colourN(optionsHasNoMinify ? "--no-minify" : "--skip-minify")} are incompatible\n`
168
- )
169
- logHelp()
170
- break
171
- }
172
- const shouldSkipMinify = options.get("no-minify") || options.get("skip-minify")
173
- let shouldMinify
174
- if (null != shouldSkipMinify) {
175
- if ("boolean" != typeof shouldSkipMinify) {
176
- logError(
177
- `The value for ${colourN(optionsHasNoMinify ? "--no-minify" : "--skip-minify")} must be ${colourV("true")} or ${colourV("false")}\n`
170
+ const scripts = commands.slice(2)
171
+ if (scripts.length) {
172
+ const invalidScript = scripts.find(
173
+ script => !/^(?:[a-z_][a-z\d_]{0,24}|\*)\.(?:[a-z_][a-z\d_]{0,24}|\*)$/.test(script)
178
174
  )
179
- logHelp()
180
- break
175
+ if (invalidScript) {
176
+ logError(`Invalid script name: ${JSON.stringify(invalidScript)}\n`)
177
+ logHelp()
178
+ process.exit(1)
179
+ }
180
+ } else scripts.push("*.*")
181
+ if ("push" == commands[0]) {
182
+ const { push, MissingSourceFolderError, MissingHackmudFolderError, NoUsersError } = await pushModule
183
+ complainAboutUnrecognisedOptions()
184
+ const infos = await push(sourcePath, hackmudPath, {
185
+ scripts,
186
+ onPush: info => logInfo(info, hackmudPath),
187
+ minify: noMinifyOption && !noMinifyOption.value,
188
+ mangleNames: mangleNamesOption?.value,
189
+ forceQuineCheats: forceQuineCheatsOption?.value
190
+ })
191
+ if (infos instanceof Error) {
192
+ logError(infos.message)
193
+ if (infos instanceof MissingSourceFolderError || infos instanceof NoUsersError) {
194
+ console.log()
195
+ logHelp()
196
+ } else
197
+ infos instanceof MissingHackmudFolderError &&
198
+ log(
199
+ `If this is not where your hackmud folder is, you can specify it with the\n${colourN("--hackmud-path")}=${colourB("<path>")} option or ${colourN("HSM_HACKMUD_PATH")} environment variable`
200
+ )
201
+ } else infos.length || logError("Could not find any scripts to push")
202
+ } else {
203
+ const typeDeclarationPathOption = popOption(
204
+ "type-declaration-path",
205
+ "type-declaration",
206
+ "dts",
207
+ "gen-types"
208
+ )
209
+ complainAboutUnrecognisedOptions()
210
+ const { watch } = await watchModule
211
+ watch(sourcePath, hackmudPath, {
212
+ scripts,
213
+ onPush: info => logInfo(info, hackmudPath),
214
+ typeDeclarationPath: typeDeclarationPathOption?.value.toString(),
215
+ minify: noMinifyOption && !noMinifyOption.value,
216
+ mangleNames: mangleNamesOption?.value,
217
+ onReady: () => log("Watching"),
218
+ forceQuineCheats: forceQuineCheatsOption?.value
219
+ })
220
+ autoExit = !1
181
221
  }
182
- shouldMinify = !shouldSkipMinify
183
222
  }
184
- const shouldMangleNames = options.get("mangle-names")
185
- if (null != shouldMangleNames && "boolean" != typeof shouldMangleNames) {
186
- logError(
187
- `The value for ${colourN("--mangle-names")} must be ${colourV("true")} or ${colourV("false")}\n`
188
- )
189
- logHelp()
190
- break
191
- }
192
- const shouldforceQuineCheats = options.get("force-quine-cheats")
193
- if (null != shouldforceQuineCheats && "boolean" != typeof shouldforceQuineCheats) {
194
- logError(
195
- `The value for ${colourN("--force-quine-cheats")} must be ${colourV("true")} or ${colourV("false")}\n`
196
- )
197
- logHelp()
198
- break
199
- }
200
- const { watch } = await watchModule
201
- watch(sourcePath, hackmudPath, {
202
- scripts,
203
- onPush: info => logInfo(info, hackmudPath),
204
- typeDeclarationPath: (
205
- options.get("type-declaration-path") ||
206
- options.get("type-declaration") ||
207
- options.get("dts") ||
208
- options.get("gen-types")
209
- )?.toString(),
210
- minify: shouldMinify,
211
- mangleNames: shouldMangleNames,
212
- onReady: () => log("Watching"),
213
- forceQuineCheats: shouldforceQuineCheats
214
- })
215
- autoExit = !1
216
223
  }
217
224
  break
218
225
  case "pull":
@@ -222,8 +229,9 @@ switch (commands[0]) {
222
229
  if (!script) {
223
230
  logError("Must provide the script to pull\n")
224
231
  logHelp()
225
- break
232
+ process.exit(1)
226
233
  }
234
+ complainAboutUnrecognisedOptions()
227
235
  const sourcePath = commands[2] || "."
228
236
  await pull(sourcePath, hackmudPath, script).catch(error => {
229
237
  console.error(error)
@@ -233,8 +241,9 @@ switch (commands[0]) {
233
241
  break
234
242
  case "sync-macros":
235
243
  {
236
- const hackmudPath = getHackmudPath(),
237
- { macrosSynced, usersSynced } = await syncMacros(hackmudPath)
244
+ const hackmudPath = getHackmudPath()
245
+ complainAboutUnrecognisedOptions()
246
+ const { macrosSynced, usersSynced } = await syncMacros(hackmudPath)
238
247
  log(`Synced ${macrosSynced} macros to ${usersSynced} users`)
239
248
  }
240
249
  break
@@ -242,19 +251,29 @@ switch (commands[0]) {
242
251
  case "gen-type-declaration":
243
252
  case "gen-dts":
244
253
  case "gen-types":
254
+ case "emit-dts":
245
255
  {
246
- const target = commands[1]
256
+ "emit-dts" != commands[0] &&
257
+ "gen-dts" != commands[0] &&
258
+ console.warn(
259
+ colourF(
260
+ `Warning: ${colourC("hsm")} ${colourL(commands[0])} is being deprecated and will be removed\n in the next minor release of HSM\n You should switch to using its alias ${colourC("hsm")} ${colourL("emit-dts")}\n`
261
+ )
262
+ )
263
+ const hackmudPath = getHackmudPath(),
264
+ target = commands[1]
247
265
  if (!target) {
248
266
  logError("Must provide target directory\n")
249
267
  logHelp()
250
- break
268
+ process.exit(1)
251
269
  }
270
+ complainAboutUnrecognisedOptions()
252
271
  const sourcePath = resolve(target),
253
272
  outputPath = commands[2] || "./player.d.ts",
254
- typeDeclaration = await generateTypeDeclaration(sourcePath, getHackmudPath())
273
+ typeDeclaration = await generateTypeDeclaration(sourcePath, hackmudPath)
255
274
  let typeDeclarationPath = resolve(outputPath)
256
275
  await writeFile(typeDeclarationPath, typeDeclaration).catch(error => {
257
- assert(error instanceof Error, "src/bin/hsm.ts:340:35")
276
+ assert(error instanceof Error, "src/bin/hsm.ts:353:35")
258
277
  if ("EISDIR" != error.code) throw error
259
278
  typeDeclarationPath = resolve(typeDeclarationPath, "player.d.ts")
260
279
  return writeFile(typeDeclarationPath, typeDeclaration)
@@ -266,99 +285,6 @@ switch (commands[0]) {
266
285
  case "h":
267
286
  logHelp()
268
287
  break
269
- case "golf":
270
- case "minify":
271
- {
272
- const target = commands[1]
273
- if (!target) {
274
- logError("Must provide target\n")
275
- logHelp()
276
- break
277
- }
278
- const fileExtension = extname(target)
279
- if (!supportedExtensions.includes(fileExtension)) {
280
- logError(
281
- `Unsupported file extension "${chalk.bold(fileExtension)}"\nSupported extensions are "${supportedExtensions.map(extension => chalk.bold(extension)).join('", "')}"`
282
- )
283
- break
284
- }
285
- const { processScript } = await processScriptModule,
286
- fileBaseName = basename(target, fileExtension),
287
- fileBaseNameEndsWithDotSrc = fileBaseName.endsWith(".src"),
288
- scriptName = fileBaseNameEndsWithDotSrc ? fileBaseName.slice(0, -4) : fileBaseName,
289
- scriptUser =
290
- "scripts" == basename(resolve(target, "..")) && "hackmud" == basename(resolve(target, "../../..")) ?
291
- basename(resolve(target, "../.."))
292
- : void 0,
293
- optionsHasNoMinify = options.has("no-minify")
294
- if ((optionsHasNoMinify || options.has("skip-minify")) && options.has("mangle-names")) {
295
- logError(
296
- `Options ${colourN("--mangle-names")} and ${colourN(optionsHasNoMinify ? "--no-minify" : "--skip-minify")} are incompatible\n`
297
- )
298
- logHelp()
299
- break
300
- }
301
- const mangleNames_ = options.get("mangle-names")
302
- if (null != mangleNames_ && "boolean" != typeof mangleNames_) {
303
- logError(
304
- `The value for ${colourN("--mangle-names")} must be ${colourV("true")} or ${colourV("false")}\n`
305
- )
306
- logHelp()
307
- break
308
- }
309
- const mangleNames = mangleNames_,
310
- forceQuineCheats_ = options.get("force-quine-cheats")
311
- if (null != forceQuineCheats_ && "boolean" != typeof forceQuineCheats_) {
312
- logError(
313
- `the value for ${colourN("--force-quine-cheats")} must be ${colourV("true")} or ${colourV("false")}\n`
314
- )
315
- logHelp()
316
- break
317
- }
318
- const forceQuineCheats = forceQuineCheats_
319
- let outputPath =
320
- commands[2] ||
321
- resolve(
322
- dirname(target),
323
- fileBaseNameEndsWithDotSrc ? scriptName + ".js"
324
- : ".js" == fileExtension ? fileBaseName + ".min.js"
325
- : fileBaseName + ".js"
326
- )
327
- const golfFile = () =>
328
- readFile(target, { encoding: "utf8" }).then(async source => {
329
- const timeStart = performance.now(),
330
- { script, warnings } = await processScript(source, {
331
- minify: !(options.get("no-minify") || options.get("skip-minify")),
332
- scriptUser,
333
- scriptName,
334
- filePath: target,
335
- mangleNames,
336
- forceQuineCheats
337
- }),
338
- timeTook = performance.now() - timeStart
339
- for (const { message, line } of warnings)
340
- log(`Warning "${chalk.bold(message)}" on line ${chalk.bold(line + "")}`)
341
- await writeFilePersistent(outputPath, script)
342
- .catch(error => {
343
- if (!commands[2] || "EISDIR" != error.code) throw error
344
- outputPath = resolve(outputPath, basename(target, fileExtension) + ".js")
345
- return writeFilePersistent(outputPath, script)
346
- })
347
- .then(() =>
348
- log(
349
- `Wrote ${chalk.bold(countHackmudCharacters(script))} chars to ${chalk.bold(relative(".", outputPath))} | took ${Math.round(100 * timeTook) / 100}ms`
350
- )
351
- )
352
- })
353
- if (options.get("watch")) {
354
- const { watch: watchFile } = await chokidarModule
355
- watchFile(target, { awaitWriteFinish: { stabilityThreshold: 100 } })
356
- .on("ready", () => log("Watching " + target))
357
- .on("change", golfFile)
358
- autoExit = !1
359
- } else await golfFile()
360
- }
361
- break
362
288
  default:
363
289
  commands[0] && logError(`Unknown command: ${colourL(commands[0])}\n`)
364
290
  logHelp()
@@ -368,21 +294,20 @@ function logHelp() {
368
294
  const pushCommandDescription = "Push scripts from a directory to hackmud user's scripts directories",
369
295
  forceQuineCheatsOptionDescription = `Force quine cheats on. Use ${colourN("--force-quine-cheats")}=${colourV("false")} to force off`,
370
296
  hackmudPathOption = `${colourN("--hackmud-path")}=${colourB("<path>")}\n Override hackmud path`
371
- console.log(colourN("Version") + colourS(": ") + colourV(version))
372
297
  switch (commands[0]) {
373
298
  case "dev":
374
299
  case "watch":
375
300
  case "push":
376
301
  console.log(
377
302
  colourS(
378
- `\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 Pushes 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 Pushes 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 Pushes 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 Pushes all scripts found in ${colourV("src")} folder to all users`
303
+ `${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 Pushes 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 Pushes 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 Pushes 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 Pushes all scripts found in ${colourV("src")} folder to all users`
379
304
  )
380
305
  )
381
306
  break
382
307
  case "pull":
383
308
  console.log(
384
309
  colourS(
385
- `\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}`
310
+ `${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}`
386
311
  )
387
312
  )
388
313
  break
@@ -390,7 +315,7 @@ function logHelp() {
390
315
  case "golf":
391
316
  console.log(
392
317
  colourS(
393
- `\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`
318
+ `${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`
394
319
  )
395
320
  )
396
321
  break
@@ -398,6 +323,14 @@ function logHelp() {
398
323
  case "gen-type-declaration":
399
324
  case "gen-dts":
400
325
  case "gen-types":
326
+ case "emit-dts":
327
+ "emit-dts" != commands[0] &&
328
+ "gen-dts" != commands[0] &&
329
+ console.warn(
330
+ colourF(
331
+ `Warning: ${colourC("hsm")} ${colourL(commands[0])} is being deprecated and will be removed\n in the next minor release of HSM\n You should switch to using its alias ${colourC("hsm")} ${colourL("emit-dts")}\n`
332
+ )
333
+ )
401
334
  console.log(
402
335
  colourS(
403
336
  `${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}`
@@ -407,14 +340,14 @@ function logHelp() {
407
340
  case "sync-macros":
408
341
  console.log(
409
342
  colourS(
410
- `\n${colourJ("Sync macros across all hackmud users")}\n\n${colourA("Options:")}\n${hackmudPathOption}`
343
+ `${colourJ("Sync macros across all hackmud users")}\n\n${colourA("Options:")}\n${hackmudPathOption}`
411
344
  )
412
345
  )
413
346
  break
414
347
  default:
415
348
  console.log(
416
349
  colourS(
417
- `\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`
350
+ `${colourJ("Hackmud Script Manager")}\n${colourN("Version") + colourS(": ") + colourV("0.20.4-6815ce0")}\n\n${colourA("Commands:")}\n${colourL("push")}\n ${pushCommandDescription}\n${colourL("dev")}\n Watch a directory and push a script when modified\n${colourL("minify")}\n Minify a script file on the spot\n${colourL("emit-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`
418
351
  )
419
352
  )
420
353
  }
@@ -432,7 +365,7 @@ function logError(message) {
432
365
  process.exitCode = 1
433
366
  }
434
367
  function getHackmudPath() {
435
- const hackmudPathOption = options.get("hackmud-path")
368
+ const hackmudPathOption = popOption("hackmud-path")?.value
436
369
  if (null != hackmudPathOption && "string" != typeof hackmudPathOption) {
437
370
  logError(`Option ${colourN("--hackmud-path")} must be a string, got ${colourV(hackmudPathOption)}\n`)
438
371
  logHelp()
@@ -444,3 +377,32 @@ function getHackmudPath() {
444
377
  ("win32" == process.platform ? resolve(process.env.APPDATA, "hackmud") : resolve(homedir(), ".config/hackmud"))
445
378
  )
446
379
  }
380
+ function assertOptionIsBoolean(option) {
381
+ if ("boolean" != typeof option.value) {
382
+ logError(`The value for ${formatOption(option.name)} must be ${colourV("true")} or ${colourV("false")}\n`)
383
+ logHelp()
384
+ process.exit(1)
385
+ }
386
+ }
387
+ function popOption(...names) {
388
+ const presentOptionNames = names.filter(name => options.has(name))
389
+ if (!presentOptionNames.length) return
390
+ const presentOptionNamesWithDashDash = presentOptionNames.map(formatOption)
391
+ if (presentOptionNames.length > 1) {
392
+ logError(
393
+ `The options ${presentOptionNamesWithDashDash.join(", ")} are aliases for each other. Please only specify one`
394
+ )
395
+ process.exit(1)
396
+ }
397
+ const value = options.get(presentOptionNames[0])
398
+ options.delete(presentOptionNames[0])
399
+ return { name: presentOptionNamesWithDashDash[0], value }
400
+ }
401
+ function complainAboutUnrecognisedOptions() {
402
+ if (options.size) {
403
+ logError(
404
+ `Unrecognised option${options.size > 1 ? "s" : ""}: ${[...options.keys()].map(formatOption).join(", ")}`
405
+ )
406
+ process.exit(1)
407
+ }
408
+ }