hackmud-script-manager 0.20.4-0e584dd → 0.20.4-1581594

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
@@ -1,5 +1,5 @@
1
1
  # Hackmud Script Manager
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.
2
+ Command made for [Hackmud Scripting Environment](https://github.com/samualtnorman/hackmud-environment), which is a scripting environment for hackmud with minification, autocompletes / intellisense, and TypeScript support.
3
3
 
4
4
  [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/R6R0XN5CX)
5
5
 
package/bin/hsm.js CHANGED
@@ -5,14 +5,15 @@ 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-0e584dd",
16
+ const version = "0.20.4-1581594",
16
17
  options = new Map(),
17
18
  commands = [],
18
19
  userColours = new Cache(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,153 +55,169 @@ 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 You should update your version of Node.js\n https://nodejs.org/en/download/package-manager"
62
+ )
63
+ )
64
+ if ("v" == commands[0] || "version" == commands[0] || popOption("version", "v")?.value) {
65
+ console.log(version)
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 ${colourN(noMinifyOption.name)} and ${colourN(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 } = await pushModule
123
- ;(
124
- await push(sourcePath, hackmudPath, {
125
- scripts,
126
- onPush: info => logInfo(info, hackmudPath),
127
- minify: shouldMinify,
128
- mangleNames: shouldMangleNames,
129
- forceQuineCheats: shouldforceQuineCheats
130
- })
131
- ).length || logError("Could not find any scripts to push")
132
- }
133
- break
134
- case "dev":
135
- case "watch":
136
- {
137
- const hackmudPath = getHackmudPath(),
138
- sourcePath = commands[1]
139
- if (!sourcePath) {
140
- logError("Must provide the directory to watch\n")
141
- logHelp()
142
- break
143
- }
144
- const scripts = commands.slice(2)
145
- if (scripts.length) {
146
- const invalidScript = scripts.find(
147
- script => !/^(?:[a-z_][a-z\d_]{0,24}|\*)\.(?:[a-z_][a-z\d_]{0,24}|\*)$/.test(script)
148
- )
149
- if (invalidScript) {
150
- logError(`Invalid script name: ${JSON.stringify(invalidScript)}\n`)
109
+ const { processScript } = await processScriptModule,
110
+ fileBaseName = basename(target, fileExtension),
111
+ fileBaseNameEndsWithDotSrc = fileBaseName.endsWith(".src"),
112
+ scriptName = fileBaseNameEndsWithDotSrc ? fileBaseName.slice(0, -4) : fileBaseName,
113
+ scriptUser =
114
+ (
115
+ "scripts" == basename(resolve(target, "..")) &&
116
+ "hackmud" == basename(resolve(target, "../../.."))
117
+ ) ?
118
+ basename(resolve(target, "../.."))
119
+ : void 0
120
+ let outputPath =
121
+ commands[2] ||
122
+ resolve(
123
+ dirname(target),
124
+ fileBaseNameEndsWithDotSrc ? scriptName + ".js"
125
+ : ".js" == fileExtension ? fileBaseName + ".min.js"
126
+ : fileBaseName + ".js"
127
+ )
128
+ const golfFile = () =>
129
+ readFile(target, { encoding: "utf8" }).then(async source => {
130
+ const timeStart = performance.now(),
131
+ { script, warnings } = await processScript(source, {
132
+ minify: noMinifyOption && !noMinifyOption.value,
133
+ scriptUser,
134
+ scriptName,
135
+ filePath: target,
136
+ mangleNames: mangleNamesOption?.value,
137
+ forceQuineCheats: forceQuineCheatsOption?.value
138
+ }),
139
+ timeTook = performance.now() - timeStart
140
+ for (const { message, line } of warnings)
141
+ log(`Warning "${chalk.bold(message)}" on line ${chalk.bold(line + "")}`)
142
+ await writeFilePersistent(outputPath, script)
143
+ .catch(error => {
144
+ if (!commands[2] || "EISDIR" != error.code) throw error
145
+ outputPath = resolve(outputPath, basename(target, fileExtension) + ".js")
146
+ return writeFilePersistent(outputPath, script)
147
+ })
148
+ .then(() =>
149
+ log(
150
+ `Wrote ${chalk.bold(countHackmudCharacters(script))} chars to ${chalk.bold(relative(".", outputPath))} | took ${Math.round(100 * timeTook) / 100}ms`
151
+ )
152
+ )
153
+ })
154
+ if (watchOption) {
155
+ const { watch: watchFile } = await chokidarModule
156
+ watchFile(target, { awaitWriteFinish: { stabilityThreshold: 100 } })
157
+ .on("ready", () => log("Watching " + target))
158
+ .on("change", golfFile)
159
+ autoExit = !1
160
+ } else await golfFile()
161
+ } else {
162
+ const hackmudPath = getHackmudPath(),
163
+ sourcePath = commands[1]
164
+ if (!sourcePath) {
165
+ logError(`Must provide the directory to ${"push" == commands[0] ? "push from" : "watch"}\n`)
151
166
  logHelp()
152
- break
167
+ process.exit(1)
153
168
  }
154
- } else scripts.push("*.*")
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
- )
160
- logHelp()
161
- break
162
- }
163
- const shouldSkipMinify = options.get("no-minify") || options.get("skip-minify")
164
- let shouldMinify
165
- if (null != shouldSkipMinify) {
166
- if ("boolean" != typeof shouldSkipMinify) {
167
- logError(
168
- `The value for ${colourN(optionsHasNoMinify ? "--no-minify" : "--skip-minify")} must be ${colourV("true")} or ${colourV("false")}\n`
169
+ const scripts = commands.slice(2)
170
+ if (scripts.length) {
171
+ const invalidScript = scripts.find(
172
+ script => !/^(?:[a-z_][a-z\d_]{0,24}|\*)\.(?:[a-z_][a-z\d_]{0,24}|\*)$/.test(script)
169
173
  )
170
- logHelp()
171
- break
174
+ if (invalidScript) {
175
+ logError(`Invalid script name: ${JSON.stringify(invalidScript)}\n`)
176
+ logHelp()
177
+ process.exit(1)
178
+ }
179
+ } else scripts.push("*.*")
180
+ if ("push" == commands[0]) {
181
+ const { push, MissingSourceFolderError, MissingHackmudFolderError, NoUsersError } =
182
+ await pushModule,
183
+ infos = await push(sourcePath, hackmudPath, {
184
+ scripts,
185
+ onPush: info => logInfo(info, hackmudPath),
186
+ minify: noMinifyOption && !noMinifyOption.value,
187
+ mangleNames: mangleNamesOption?.value,
188
+ forceQuineCheats: forceQuineCheatsOption?.value
189
+ })
190
+ if (infos instanceof Error) {
191
+ logError(infos.message)
192
+ if (infos instanceof MissingSourceFolderError || infos instanceof NoUsersError) {
193
+ console.log()
194
+ logHelp()
195
+ } else
196
+ infos instanceof MissingHackmudFolderError &&
197
+ log(
198
+ `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`
199
+ )
200
+ } else infos.length || logError("Could not find any scripts to push")
201
+ } else {
202
+ const typeDeclarationPathOption = popOption(
203
+ "type-declaration-path",
204
+ "type-declaration",
205
+ "dts",
206
+ "gen-types"
207
+ ),
208
+ { watch } = await watchModule
209
+ watch(sourcePath, hackmudPath, {
210
+ scripts,
211
+ onPush: info => logInfo(info, hackmudPath),
212
+ typeDeclarationPath: typeDeclarationPathOption?.value.toString(),
213
+ minify: noMinifyOption && !noMinifyOption.value,
214
+ mangleNames: mangleNamesOption?.value,
215
+ onReady: () => log("Watching"),
216
+ forceQuineCheats: forceQuineCheatsOption?.value
217
+ })
218
+ autoExit = !1
172
219
  }
173
- shouldMinify = !shouldSkipMinify
174
- }
175
- const shouldMangleNames = options.get("mangle-names")
176
- if (null != shouldMangleNames && "boolean" != typeof shouldMangleNames) {
177
- logError(
178
- `The value for ${colourN("--mangle-names")} must be ${colourV("true")} or ${colourV("false")}\n`
179
- )
180
- logHelp()
181
- break
182
- }
183
- const shouldforceQuineCheats = options.get("force-quine-cheats")
184
- if (null != shouldforceQuineCheats && "boolean" != typeof shouldforceQuineCheats) {
185
- logError(
186
- `The value for ${colourN("--force-quine-cheats")} must be ${colourV("true")} or ${colourV("false")}\n`
187
- )
188
- logHelp()
189
- break
190
220
  }
191
- const { watch } = await watchModule
192
- watch(sourcePath, hackmudPath, {
193
- scripts,
194
- onPush: info => logInfo(info, hackmudPath),
195
- typeDeclarationPath: (
196
- options.get("type-declaration-path") ||
197
- options.get("type-declaration") ||
198
- options.get("dts") ||
199
- options.get("gen-types")
200
- )?.toString(),
201
- minify: shouldMinify,
202
- mangleNames: shouldMangleNames,
203
- onReady: () => log("Watching"),
204
- forceQuineCheats: shouldforceQuineCheats
205
- })
206
- autoExit = !1
207
221
  }
208
222
  break
209
223
  case "pull":
@@ -213,7 +227,7 @@ switch (commands[0]) {
213
227
  if (!script) {
214
228
  logError("Must provide the script to pull\n")
215
229
  logHelp()
216
- break
230
+ process.exit(1)
217
231
  }
218
232
  const sourcePath = commands[2] || "."
219
233
  await pull(sourcePath, hackmudPath, script).catch(error => {
@@ -234,18 +248,19 @@ switch (commands[0]) {
234
248
  case "gen-dts":
235
249
  case "gen-types":
236
250
  {
237
- const target = commands[1]
251
+ const hackmudPath = getHackmudPath(),
252
+ target = commands[1]
238
253
  if (!target) {
239
254
  logError("Must provide target directory\n")
240
255
  logHelp()
241
- break
256
+ process.exit(1)
242
257
  }
243
258
  const sourcePath = resolve(target),
244
259
  outputPath = commands[2] || "./player.d.ts",
245
- typeDeclaration = await generateTypeDeclaration(sourcePath, getHackmudPath())
260
+ typeDeclaration = await generateTypeDeclaration(sourcePath, hackmudPath)
246
261
  let typeDeclarationPath = resolve(outputPath)
247
262
  await writeFile(typeDeclarationPath, typeDeclaration).catch(error => {
248
- assert(error instanceof Error, "src/bin/hsm.ts:327:35")
263
+ assert(error instanceof Error, "src/bin/hsm.ts:330:35")
249
264
  if ("EISDIR" != error.code) throw error
250
265
  typeDeclarationPath = resolve(typeDeclarationPath, "player.d.ts")
251
266
  return writeFile(typeDeclarationPath, typeDeclaration)
@@ -257,99 +272,6 @@ switch (commands[0]) {
257
272
  case "h":
258
273
  logHelp()
259
274
  break
260
- case "golf":
261
- case "minify":
262
- {
263
- const target = commands[1]
264
- if (!target) {
265
- logError("Must provide target\n")
266
- logHelp()
267
- break
268
- }
269
- const fileExtension = extname(target)
270
- if (!supportedExtensions.includes(fileExtension)) {
271
- logError(
272
- `Unsupported file extension "${chalk.bold(fileExtension)}"\nSupported extensions are "${supportedExtensions.map(extension => chalk.bold(extension)).join('", "')}"`
273
- )
274
- break
275
- }
276
- const { processScript } = await processScriptModule,
277
- fileBaseName = basename(target, fileExtension),
278
- fileBaseNameEndsWithDotSrc = fileBaseName.endsWith(".src"),
279
- scriptName = fileBaseNameEndsWithDotSrc ? fileBaseName.slice(0, -4) : fileBaseName,
280
- scriptUser =
281
- "scripts" == basename(resolve(target, "..")) && "hackmud" == basename(resolve(target, "../../..")) ?
282
- basename(resolve(target, "../.."))
283
- : void 0,
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
- )
289
- logHelp()
290
- break
291
- }
292
- const mangleNames_ = options.get("mangle-names")
293
- if (null != mangleNames_ && "boolean" != typeof mangleNames_) {
294
- logError(
295
- `The value for ${colourN("--mangle-names")} must be ${colourV("true")} or ${colourV("false")}\n`
296
- )
297
- logHelp()
298
- break
299
- }
300
- const mangleNames = mangleNames_,
301
- forceQuineCheats_ = options.get("force-quine-cheats")
302
- if (null != forceQuineCheats_ && "boolean" != typeof forceQuineCheats_) {
303
- logError(
304
- `the value for ${colourN("--force-quine-cheats")} must be ${colourV("true")} or ${colourV("false")}\n`
305
- )
306
- logHelp()
307
- break
308
- }
309
- const forceQuineCheats = forceQuineCheats_
310
- let outputPath =
311
- commands[2] ||
312
- resolve(
313
- dirname(target),
314
- fileBaseNameEndsWithDotSrc ? scriptName + ".js"
315
- : ".js" == fileExtension ? fileBaseName + ".min.js"
316
- : fileBaseName + ".js"
317
- )
318
- const golfFile = () =>
319
- readFile(target, { encoding: "utf8" }).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`
341
- )
342
- )
343
- })
344
- if (options.get("watch")) {
345
- const { watch: watchFile } = await chokidarModule
346
- watchFile(target, { awaitWriteFinish: { stabilityThreshold: 100 } })
347
- .on("ready", () => log("Watching " + target))
348
- .on("change", golfFile)
349
- autoExit = !1
350
- } else await golfFile()
351
- }
352
- break
353
275
  default:
354
276
  commands[0] && logError(`Unknown command: ${colourL(commands[0])}\n`)
355
277
  logHelp()
@@ -423,7 +345,7 @@ function logError(message) {
423
345
  process.exitCode = 1
424
346
  }
425
347
  function getHackmudPath() {
426
- const hackmudPathOption = options.get("hackmud-path")
348
+ const hackmudPathOption = popOption("hackmud-path")
427
349
  if (null != hackmudPathOption && "string" != typeof hackmudPathOption) {
428
350
  logError(`Option ${colourN("--hackmud-path")} must be a string, got ${colourV(hackmudPathOption)}\n`)
429
351
  logHelp()
@@ -435,3 +357,26 @@ function getHackmudPath() {
435
357
  ("win32" == process.platform ? resolve(process.env.APPDATA, "hackmud") : resolve(homedir(), ".config/hackmud"))
436
358
  )
437
359
  }
360
+ function assertOptionIsBoolean(option) {
361
+ if ("boolean" != typeof option.value) {
362
+ logError(`The value for ${colourN(option.name)} must be ${colourV("true")} or ${colourV("false")}\n`)
363
+ logHelp()
364
+ process.exit(1)
365
+ }
366
+ }
367
+ function popOption(...names) {
368
+ const presentOptionNames = names.filter(name => options.has(name))
369
+ if (!presentOptionNames.length) return
370
+ const presentOptionNamesWithDashDash = presentOptionNames.map(name =>
371
+ colourN(`-${1 == name.length ? "" : "-"}${name}`)
372
+ )
373
+ if (presentOptionNames.length > 1) {
374
+ logError(
375
+ `The options ${presentOptionNamesWithDashDash.join(", ")} are aliases for each other. Please only specify one`
376
+ )
377
+ process.exit(1)
378
+ }
379
+ const value = options.get(presentOptionNames[0])
380
+ options.delete(presentOptionNames[0])
381
+ return { name: presentOptionNamesWithDashDash[0], value }
382
+ }