hackmud-script-manager 0.20.4-8b61cf2 → 0.20.4-8b994a4

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,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-8b61cf2",
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)
@@ -23,23 +24,19 @@ const version = "0.20.4-8b61cf2",
23
24
  log = message => console.log(colourS(message))
24
25
  for (const argument of process.argv.slice(2))
25
26
  if ("-" == argument[0]) {
26
- const [key, valueRaw] = argument.split("=")
27
- let value = valueRaw
28
- if (value)
29
- if ("true" == value) value = !0
30
- else if ("false" == value) value = !1
31
- else {
32
- const number = Number(value)
33
- isFinite(number) && (value = number)
34
- }
35
- else value = !0
27
+ const argumentEqualsIndex = argument.indexOf("=")
28
+ let key, value
29
+ if (-1 == argumentEqualsIndex) {
30
+ key = argument
31
+ value = !0
32
+ } else {
33
+ key = argument.slice(0, argumentEqualsIndex)
34
+ value = argument.slice(argumentEqualsIndex + 1)
35
+ "true" == value ? (value = !0) : "false" == value && (value = !1)
36
+ }
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,227 @@ 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
+ `${chalk.bold("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-8b994a4")
66
+ process.exit()
67
+ }
68
+ let warnedDeprecatedEmitDtsAlias = !1
69
+ if (popOption("help", "h")?.value) {
61
70
  logHelp()
62
71
  process.exit()
63
72
  }
64
73
  let autoExit = !0
65
74
  switch (commands[0]) {
66
75
  case "push":
76
+ case "dev":
77
+ case "watch":
78
+ case "golf":
79
+ case "minify":
67
80
  {
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")) {
88
- logError(
89
- `Options ${colourN("--mangle-names")} and ${colourN(optionsHasNoMinify ? "--no-minify" : "--skip-minify")} are incompatible\n`
90
- )
91
- logHelp()
92
- break
93
- }
94
- const shouldSkipMinify = options.get("no-minify") || options.get("skip-minify")
95
- let shouldMinify
96
- if (null != shouldSkipMinify) {
97
- if ("boolean" != typeof shouldSkipMinify) {
98
- logError(
99
- `The value for ${colourN(optionsHasNoMinify ? "--no-minify" : "--skip-minify")} must be ${colourV("true")} or ${colourV("false")}\n`
81
+ const noMinifyOption = popOption("no-minify", "skip-minify")
82
+ noMinifyOption &&
83
+ "no-minify" != noMinifyOption.name &&
84
+ console.warn(
85
+ colourF(
86
+ `${chalk.bold("Warning:")} ${formatOption(noMinifyOption.name)} is deprecated and will be removed in the next minor\n release of HSM\n You should switch to using its alias ${colourN("--no-minify")}\n`
100
87
  )
101
- logHelp()
102
- break
103
- }
104
- shouldMinify = !shouldSkipMinify
105
- }
106
- const shouldMangleNames = options.get("mangle-names")
107
- if (null != shouldMangleNames && "boolean" != typeof shouldMangleNames) {
88
+ )
89
+ const mangleNamesOption = popOption("mangle-names"),
90
+ forceQuineCheatsOption = popOption("force-quine-cheats"),
91
+ noQuineCheatsOptions = popOption("no-quine-cheats"),
92
+ noMinifyIncompatibleOption = mangleNamesOption || forceQuineCheatsOption || noQuineCheatsOptions
93
+ if (noMinifyOption && noMinifyIncompatibleOption) {
108
94
  logError(
109
- `The value for ${colourN("--mangle-names")} must be ${colourV("true")} or ${colourV("false")}\n`
95
+ `Options ${formatOption(noMinifyOption.name)} and ${formatOption(noMinifyIncompatibleOption.name)} are incompatible\n`
110
96
  )
111
97
  logHelp()
112
- break
98
+ process.exit(1)
113
99
  }
114
- const shouldforceQuineCheats = options.get("force-quine-cheats")
115
- if (null != shouldforceQuineCheats && "boolean" != typeof shouldforceQuineCheats) {
100
+ if (forceQuineCheatsOption && noQuineCheatsOptions) {
116
101
  logError(
117
- `The value for ${colourN("--force-quine-cheats")} must be ${colourV("true")} or ${colourV("false")}\n`
102
+ `Options ${formatOption(forceQuineCheatsOption.name)} and ${formatOption(noQuineCheatsOptions.name)} are incompatible\n`
118
103
  )
119
104
  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
105
+ process.exit(1)
152
106
  }
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`)
107
+ noMinifyOption && assertOptionIsBoolean(noMinifyOption)
108
+ mangleNamesOption && assertOptionIsBoolean(mangleNamesOption)
109
+ forceQuineCheatsOption && assertOptionIsBoolean(forceQuineCheatsOption)
110
+ noQuineCheatsOptions && assertOptionIsBoolean(noQuineCheatsOptions)
111
+ const rootFolderPathOption = popOption("root-folder-path"),
112
+ rootFolderPath = rootFolderPathOption && resolve(rootFolderPathOption.value + "")
113
+ if ("golf" == commands[0] || "minify" == commands[0]) {
114
+ const watchOption = popOption("watch"),
115
+ target = commands[1]
116
+ if (!target) {
117
+ logError("Must provide target\n")
160
118
  logHelp()
161
- break
119
+ process.exit(1)
162
120
  }
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) {
121
+ const fileExtension = extname(target)
122
+ if (!supportedExtensions.includes(fileExtension)) {
176
123
  logError(
177
- `The value for ${colourN(optionsHasNoMinify ? "--no-minify" : "--skip-minify")} must be ${colourV("true")} or ${colourV("false")}\n`
124
+ `Unsupported file extension "${chalk.bold(fileExtension)}"\nSupported extensions are "${supportedExtensions.map(extension => chalk.bold(extension)).join('", "')}"`
178
125
  )
126
+ process.exit(1)
127
+ }
128
+ complainAboutUnrecognisedOptions()
129
+ const { processScript } = await processScriptModule,
130
+ fileBaseName = basename(target, fileExtension),
131
+ fileBaseNameEndsWithDotSrc = fileBaseName.endsWith(".src"),
132
+ scriptName = fileBaseNameEndsWithDotSrc ? fileBaseName.slice(0, -4) : fileBaseName,
133
+ scriptUser =
134
+ (
135
+ "scripts" == basename(resolve(target, "..")) &&
136
+ "hackmud" == basename(resolve(target, "../../.."))
137
+ ) ?
138
+ basename(resolve(target, "../.."))
139
+ : void 0
140
+ let outputPath =
141
+ commands[2] ||
142
+ resolve(
143
+ dirname(target),
144
+ fileBaseNameEndsWithDotSrc ? scriptName + ".js"
145
+ : ".js" == fileExtension ? fileBaseName + ".min.js"
146
+ : fileBaseName + ".js"
147
+ )
148
+ const golfFile = () =>
149
+ readFile(target, { encoding: "utf8" }).then(async source => {
150
+ const timeStart = performance.now(),
151
+ { script, warnings } = await processScript(source, {
152
+ minify: noMinifyOption && !noMinifyOption.value,
153
+ scriptUser,
154
+ scriptName,
155
+ filePath: target,
156
+ mangleNames: mangleNamesOption?.value,
157
+ forceQuineCheats: forceQuineCheatsOption?.value ?? !noQuineCheatsOptions?.value,
158
+ rootFolderPath
159
+ }),
160
+ timeTook = performance.now() - timeStart
161
+ for (const { message } of warnings)
162
+ console.warn(colourF(`${chalk.bold("Warning:")} ${message}`))
163
+ await writeFilePersistent(outputPath, script)
164
+ .catch(error => {
165
+ if (!commands[2] || "EISDIR" != error.code) throw error
166
+ outputPath = resolve(outputPath, basename(target, fileExtension) + ".js")
167
+ return writeFilePersistent(outputPath, script)
168
+ })
169
+ .then(() =>
170
+ log(
171
+ `Wrote ${chalk.bold(countHackmudCharacters(script))} chars to ${chalk.bold(relative(".", outputPath))} | took ${Math.round(100 * timeTook) / 100}ms`
172
+ )
173
+ )
174
+ })
175
+ if (watchOption) {
176
+ const { watch: watchFile } = await chokidarModule
177
+ watchFile(target, { awaitWriteFinish: { stabilityThreshold: 100 } })
178
+ .on("ready", () => log("Watching " + target))
179
+ .on("change", golfFile)
180
+ autoExit = !1
181
+ } else await golfFile()
182
+ } else {
183
+ const hackmudPath = getHackmudPath(),
184
+ sourcePath = commands[1]
185
+ if (!sourcePath) {
186
+ logError(`Must provide the directory to ${"push" == commands[0] ? "push from" : "watch"}\n`)
179
187
  logHelp()
180
- break
188
+ process.exit(1)
189
+ }
190
+ const scripts = commands.slice(2)
191
+ if (scripts.length) {
192
+ const invalidScript = scripts.find(
193
+ script => !/^(?:[a-z_][a-z\d_]{0,24}|\*)\.(?:[a-z_][a-z\d_]{0,24}|\*)$/.test(script)
194
+ )
195
+ if (invalidScript) {
196
+ logError(`Invalid script name: ${JSON.stringify(invalidScript)}\n`)
197
+ logHelp()
198
+ process.exit(1)
199
+ }
200
+ } else scripts.push("*.*")
201
+ const watchOption = popOption("watch")
202
+ if ("push" != commands[0] || watchOption?.value) {
203
+ const dtsPathOption = popOption(
204
+ "dts-path",
205
+ "type-declaration-path",
206
+ "type-declaration",
207
+ "dts",
208
+ "gen-types"
209
+ )
210
+ dtsPathOption &&
211
+ "dts-path" != dtsPathOption.name &&
212
+ "type-declaration-path" != dtsPathOption.name &&
213
+ console.warn(
214
+ colourF(
215
+ `${chalk.bold("Warning:")} ${formatOption(dtsPathOption.name)} is deprecated and will be removed in the\n next minor release of HSM\n You should switch to using its alias ${colourN("--dts-path")}\n`
216
+ )
217
+ )
218
+ complainAboutUnrecognisedOptions()
219
+ const { watch } = await watchModule
220
+ watch(sourcePath, hackmudPath, {
221
+ scripts,
222
+ onPush: info => logInfo(info, hackmudPath),
223
+ typeDeclarationPath: dtsPathOption?.value.toString(),
224
+ minify: noMinifyOption && !noMinifyOption.value,
225
+ mangleNames: mangleNamesOption?.value,
226
+ onReady: () => log("Watching"),
227
+ forceQuineCheats: forceQuineCheatsOption?.value ?? !noQuineCheatsOptions?.value,
228
+ rootFolderPath
229
+ })
230
+ autoExit = !1
231
+ } else {
232
+ const dtsPathOption = popOption("dts-path")
233
+ complainAboutUnrecognisedOptions()
234
+ let declarationPathPromise
235
+ if (dtsPathOption) {
236
+ if ("string" != typeof dtsPathOption.value) {
237
+ logError(
238
+ `Option ${formatOption(dtsPathOption.name)} must be a string, got ${colourV(dtsPathOption.value)}\n`
239
+ )
240
+ logHelp()
241
+ process.exit(1)
242
+ }
243
+ let typeDeclarationPath = resolve(dtsPathOption.value)
244
+ const typeDeclaration = await generateTypeDeclaration(sourcePath, hackmudPath)
245
+ declarationPathPromise = writeFile(typeDeclarationPath, typeDeclaration)
246
+ .catch(error => {
247
+ assert(error instanceof Error, "src/bin/hsm.ts:292:38")
248
+ if ("EISDIR" != error.code) throw error
249
+ typeDeclarationPath = resolve(typeDeclarationPath, "player.d.ts")
250
+ return writeFile(typeDeclarationPath, typeDeclaration)
251
+ })
252
+ .then(() => typeDeclarationPath)
253
+ }
254
+ const { push, MissingSourceFolderError, MissingHackmudFolderError, NoUsersError } =
255
+ await pushModule,
256
+ infos = await push(sourcePath, hackmudPath, {
257
+ scripts,
258
+ onPush: info => logInfo(info, hackmudPath),
259
+ minify: noMinifyOption && !noMinifyOption.value,
260
+ mangleNames: mangleNamesOption?.value,
261
+ forceQuineCheats: forceQuineCheatsOption?.value ?? !noQuineCheatsOptions?.value,
262
+ rootFolderPath
263
+ })
264
+ if (infos instanceof Error) {
265
+ logError(infos.message)
266
+ if (infos instanceof MissingSourceFolderError || infos instanceof NoUsersError) {
267
+ console.log()
268
+ logHelp()
269
+ } else
270
+ infos instanceof MissingHackmudFolderError &&
271
+ log(
272
+ `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`
273
+ )
274
+ } else infos.length || logError("Could not find any scripts to push")
275
+ declarationPathPromise &&
276
+ log("Wrote type declaration to " + chalk.bold(await declarationPathPromise))
181
277
  }
182
- shouldMinify = !shouldSkipMinify
183
- }
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
278
  }
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
279
  }
217
280
  break
218
281
  case "pull":
@@ -222,8 +285,9 @@ switch (commands[0]) {
222
285
  if (!script) {
223
286
  logError("Must provide the script to pull\n")
224
287
  logHelp()
225
- break
288
+ process.exit(1)
226
289
  }
290
+ complainAboutUnrecognisedOptions()
227
291
  const sourcePath = commands[2] || "."
228
292
  await pull(sourcePath, hackmudPath, script).catch(error => {
229
293
  console.error(error)
@@ -233,8 +297,9 @@ switch (commands[0]) {
233
297
  break
234
298
  case "sync-macros":
235
299
  {
236
- const hackmudPath = getHackmudPath(),
237
- { macrosSynced, usersSynced } = await syncMacros(hackmudPath)
300
+ const hackmudPath = getHackmudPath()
301
+ complainAboutUnrecognisedOptions()
302
+ const { macrosSynced, usersSynced } = await syncMacros(hackmudPath)
238
303
  log(`Synced ${macrosSynced} macros to ${usersSynced} users`)
239
304
  }
240
305
  break
@@ -242,19 +307,30 @@ switch (commands[0]) {
242
307
  case "gen-type-declaration":
243
308
  case "gen-dts":
244
309
  case "gen-types":
310
+ case "emit-dts":
245
311
  {
246
- const target = commands[1]
312
+ if ("emit-dts" != commands[0] && "gen-dts" != commands[0]) {
313
+ warnedDeprecatedEmitDtsAlias = !0
314
+ console.warn(
315
+ colourF(
316
+ `${chalk.bold("Warning:")} ${colourC("hsm")} ${colourL(commands[0])} is 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`
317
+ )
318
+ )
319
+ }
320
+ const hackmudPath = getHackmudPath(),
321
+ target = commands[1]
247
322
  if (!target) {
248
323
  logError("Must provide target directory\n")
249
324
  logHelp()
250
- break
325
+ process.exit(1)
251
326
  }
327
+ complainAboutUnrecognisedOptions()
252
328
  const sourcePath = resolve(target),
253
329
  outputPath = commands[2] || "./player.d.ts",
254
- typeDeclaration = await generateTypeDeclaration(sourcePath, getHackmudPath())
330
+ typeDeclaration = await generateTypeDeclaration(sourcePath, hackmudPath)
255
331
  let typeDeclarationPath = resolve(outputPath)
256
332
  await writeFile(typeDeclarationPath, typeDeclaration).catch(error => {
257
- assert(error instanceof Error, "src/bin/hsm.ts:340:35")
333
+ assert(error instanceof Error, "src/bin/hsm.ts:428:35")
258
334
  if ("EISDIR" != error.code) throw error
259
335
  typeDeclarationPath = resolve(typeDeclarationPath, "player.d.ts")
260
336
  return writeFile(typeDeclarationPath, typeDeclaration)
@@ -263,102 +339,8 @@ switch (commands[0]) {
263
339
  }
264
340
  break
265
341
  case "help":
266
- case "h":
267
342
  logHelp()
268
343
  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
344
  default:
363
345
  commands[0] && logError(`Unknown command: ${colourL(commands[0])}\n`)
364
346
  logHelp()
@@ -366,23 +348,21 @@ switch (commands[0]) {
366
348
  autoExit && process.exit()
367
349
  function logHelp() {
368
350
  const pushCommandDescription = "Push scripts from a directory to hackmud user's scripts directories",
369
- forceQuineCheatsOptionDescription = `Force quine cheats on. Use ${colourN("--force-quine-cheats")}=${colourV("false")} to force off`,
370
351
  hackmudPathOption = `${colourN("--hackmud-path")}=${colourB("<path>")}\n Override hackmud path`
371
- console.log(colourN("Version") + colourS(": ") + colourV(version))
372
352
  switch (commands[0]) {
373
353
  case "dev":
374
354
  case "watch":
375
355
  case "push":
376
356
  console.log(
377
357
  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`
358
+ `${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")}, ${colourN("--no-quine-cheats")}\n Force quine cheats on or off\n${hackmudPathOption}\n${colourN("--dts-path")}=${colourB("<path>")}\n Path to generate a type declaration (.d.ts) file for the scripts\n${colourN("--watch")}\n Watch for changes\n${colourN("--root-folder-path")}\n The folder that root will be aliased to in import statements\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
359
  )
380
360
  )
381
361
  break
382
362
  case "pull":
383
363
  console.log(
384
364
  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}`
365
+ `${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
366
  )
387
367
  )
388
368
  break
@@ -390,7 +370,7 @@ function logHelp() {
390
370
  case "golf":
391
371
  console.log(
392
372
  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`
373
+ `${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")}, ${colourN("--no-quine-cheats")}\n Force quine cheats on or off\n${colourN("--watch")}\n Watch for changes\n${colourN("--root-folder-path")}\n The folder that root will be aliased to in import statements`
394
374
  )
395
375
  )
396
376
  break
@@ -398,6 +378,15 @@ function logHelp() {
398
378
  case "gen-type-declaration":
399
379
  case "gen-dts":
400
380
  case "gen-types":
381
+ case "emit-dts":
382
+ warnedDeprecatedEmitDtsAlias ||
383
+ "emit-dts" == commands[0] ||
384
+ "gen-dts" == commands[0] ||
385
+ console.warn(
386
+ colourF(
387
+ `${chalk.bold("Warning:")} ${colourC("hsm")} ${colourL(commands[0])} is 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`
388
+ )
389
+ )
401
390
  console.log(
402
391
  colourS(
403
392
  `${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,40 +396,82 @@ function logHelp() {
407
396
  case "sync-macros":
408
397
  console.log(
409
398
  colourS(
410
- `\n${colourJ("Sync macros across all hackmud users")}\n\n${colourA("Options:")}\n${hackmudPathOption}`
399
+ `${colourJ("Sync macros across all hackmud users")}\n\n${colourA("Options:")}\n${hackmudPathOption}`
411
400
  )
412
401
  )
413
402
  break
414
403
  default:
415
404
  console.log(
416
405
  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`
406
+ `${colourJ("Hackmud Script Manager")}\n${colourN("Version") + colourS(": ") + colourV("0.20.4-8b994a4")}\n\n${colourA("Commands:")}\n${colourL("push")}\n ${pushCommandDescription}\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\n\n${colourA("Options:")}\n${colourN("--help")}\n Can be used on any command e.g. ${colourC("hsm")} ${colourL("push")} ${colourN("--help")} to show helpful information`
418
407
  )
419
408
  )
420
409
  }
421
410
  }
422
- function logInfo({ path, users, characterCount, error }, hackmudPath) {
411
+ function logInfo({ path, users, characterCount, error, warnings }, hackmudPath) {
423
412
  path = relative(".", path)
424
- error ?
425
- logError(`Error "${chalk.bold(error.message)}" in ${chalk.bold(path)}`)
426
- : log(
413
+ if (error) logError(`Error "${chalk.bold(error.message)}" in ${chalk.bold(path)}`)
414
+ else {
415
+ for (const warning of warnings) console.warn(colourF(`${chalk.bold("Warning:")} ${warning.message}`))
416
+ log(
427
417
  `Pushed ${chalk.bold(path)} to ${users.map(user => chalk.bold(userColours.get(user))).join(", ")} | ${chalk.bold(characterCount + "")} chars | ${chalk.bold(resolve(hackmudPath, users[0], "scripts", basename(path, extname(path))) + ".js")}`
428
418
  )
419
+ }
429
420
  }
430
421
  function logError(message) {
431
422
  console.error(colourD(message))
432
423
  process.exitCode = 1
433
424
  }
434
425
  function getHackmudPath() {
435
- const hackmudPathOption = options.get("hackmud-path")
436
- if (null != hackmudPathOption && "string" != typeof hackmudPathOption) {
437
- logError(`Option ${colourN("--hackmud-path")} must be a string, got ${colourV(hackmudPathOption)}\n`)
426
+ const hackmudPathOption = popOption("hackmud-path")
427
+ if (hackmudPathOption) {
428
+ if ("string" != typeof hackmudPathOption.value) {
429
+ logError(`Option ${colourN("--hackmud-path")} must be a string, got ${colourV(hackmudPathOption.value)}\n`)
430
+ logHelp()
431
+ process.exit(1)
432
+ }
433
+ if (!hackmudPathOption.value) {
434
+ logError(`Option ${colourN("--hackmud-path")} was specified but empty\n`)
435
+ logHelp()
436
+ process.exit(1)
437
+ }
438
+ return hackmudPathOption.value
439
+ }
440
+ if (null != process.env.HSM_HACKMUD_PATH) {
441
+ if (!process.env.HSM_HACKMUD_PATH) {
442
+ logError(`Environment variable ${colourN("HSM_HACKMUD_PATH")} was specified but empty\n`)
443
+ logHelp()
444
+ process.exit(1)
445
+ }
446
+ return process.env.HSM_HACKMUD_PATH
447
+ }
448
+ return "win32" == process.platform ? resolve(process.env.APPDATA, "hackmud") : resolve(homedir(), ".config/hackmud")
449
+ }
450
+ function assertOptionIsBoolean(option) {
451
+ if ("boolean" != typeof option.value) {
452
+ logError(`The value for ${formatOption(option.name)} must be ${colourV("true")} or ${colourV("false")}\n`)
438
453
  logHelp()
439
454
  process.exit(1)
440
455
  }
441
- return (
442
- hackmudPathOption ||
443
- process.env.HSM_HACKMUD_PATH ||
444
- ("win32" == process.platform ? resolve(process.env.APPDATA, "hackmud") : resolve(homedir(), ".config/hackmud"))
445
- )
456
+ }
457
+ function popOption(...names) {
458
+ const presentOptionNames = names.filter(name => options.has(name))
459
+ if (!presentOptionNames.length) return
460
+ if (presentOptionNames.length > 1) {
461
+ logError(
462
+ `The options ${presentOptionNames.map(formatOption).join(", ")} are aliases for each other. Please only specify one`
463
+ )
464
+ process.exit(1)
465
+ }
466
+ const value = options.get(presentOptionNames[0])
467
+ options.delete(presentOptionNames[0])
468
+ return { name: presentOptionNames[0], value }
469
+ }
470
+ function complainAboutUnrecognisedOptions() {
471
+ if (options.size) {
472
+ logError(
473
+ `Unrecognised option${options.size > 1 ? "s" : ""}: ${[...options.keys()].map(formatOption).join(", ")}`
474
+ )
475
+ process.exit(1)
476
+ }
446
477
  }