hackmud-script-manager 0.20.4-a7d8309 → 0.20.4-ae3052c

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
@@ -24,16 +24,16 @@ const formatOption = name => colourN(`-${1 == name.length ? "" : "-"}${name}`),
24
24
  log = message => console.log(colourS(message))
25
25
  for (const argument of process.argv.slice(2))
26
26
  if ("-" == argument[0]) {
27
- const [key, valueRaw] = argument.split("=")
28
- let value = valueRaw
29
- if (value)
30
- if ("true" == value) value = !0
31
- else if ("false" == value) value = !1
32
- else {
33
- const number = Number(value)
34
- isFinite(number) && (value = number)
35
- }
36
- 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
+ }
37
37
  if ("-" == argument[1]) options.set(key.slice(2), value)
38
38
  else for (const option of key.slice(1)) options.set(option, value)
39
39
  } else commands.push(argument)
@@ -62,9 +62,10 @@ process.version.startsWith("v21.") &&
62
62
  )
63
63
  )
64
64
  if ("v" == commands[0] || "version" == commands[0] || popOption("version", "v")?.value) {
65
- console.log("0.20.4-a7d8309")
65
+ console.log("0.20.4-ae3052c")
66
66
  process.exit()
67
67
  }
68
+ let warnedDeprecatedEmitDtsAlias = !1
68
69
  if (popOption("help", "h")?.value) {
69
70
  logHelp()
70
71
  process.exit()
@@ -77,10 +78,18 @@ switch (commands[0]) {
77
78
  case "golf":
78
79
  case "minify":
79
80
  {
80
- const noMinifyOption = popOption("no-minify", "skip-minify"),
81
- mangleNamesOption = popOption("mangle-names"),
81
+ const noMinifyOption = popOption("no-minify", "skip-minify")
82
+ noMinifyOption &&
83
+ "no-minify" != noMinifyOption.name &&
84
+ console.warn(
85
+ colourF(
86
+ `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`
87
+ )
88
+ )
89
+ const mangleNamesOption = popOption("mangle-names"),
82
90
  forceQuineCheatsOption = popOption("force-quine-cheats"),
83
- noMinifyIncompatibleOption = mangleNamesOption || forceQuineCheatsOption
91
+ noQuineCheatsOptions = popOption("no-quine-cheats"),
92
+ noMinifyIncompatibleOption = mangleNamesOption || forceQuineCheatsOption || noQuineCheatsOptions
84
93
  if (noMinifyOption && noMinifyIncompatibleOption) {
85
94
  logError(
86
95
  `Options ${formatOption(noMinifyOption.name)} and ${formatOption(noMinifyIncompatibleOption.name)} are incompatible\n`
@@ -88,9 +97,17 @@ switch (commands[0]) {
88
97
  logHelp()
89
98
  process.exit(1)
90
99
  }
100
+ if (forceQuineCheatsOption && noQuineCheatsOptions) {
101
+ logError(
102
+ `Options ${formatOption(forceQuineCheatsOption.name)} and ${formatOption(noQuineCheatsOptions.name)} are incompatible\n`
103
+ )
104
+ logHelp()
105
+ process.exit(1)
106
+ }
91
107
  noMinifyOption && assertOptionIsBoolean(noMinifyOption)
92
108
  mangleNamesOption && assertOptionIsBoolean(mangleNamesOption)
93
109
  forceQuineCheatsOption && assertOptionIsBoolean(forceQuineCheatsOption)
110
+ noQuineCheatsOptions && assertOptionIsBoolean(noQuineCheatsOptions)
94
111
  if ("golf" == commands[0] || "minify" == commands[0]) {
95
112
  const watchOption = popOption("watch"),
96
113
  target = commands[1]
@@ -135,11 +152,10 @@ switch (commands[0]) {
135
152
  scriptName,
136
153
  filePath: target,
137
154
  mangleNames: mangleNamesOption?.value,
138
- forceQuineCheats: forceQuineCheatsOption?.value
155
+ forceQuineCheats: forceQuineCheatsOption?.value ?? !noQuineCheatsOptions?.value
139
156
  }),
140
157
  timeTook = performance.now() - timeStart
141
- for (const { message, line } of warnings)
142
- log(`Warning "${chalk.bold(message)}" on line ${chalk.bold(line + "")}`)
158
+ for (const { message } of warnings) log("Warning: " + chalk.bold(message))
143
159
  await writeFilePersistent(outputPath, script)
144
160
  .catch(error => {
145
161
  if (!commands[2] || "EISDIR" != error.code) throw error
@@ -178,28 +194,8 @@ switch (commands[0]) {
178
194
  process.exit(1)
179
195
  }
180
196
  } 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 {
197
+ const watchOption = popOption("watch")
198
+ if ("push" != commands[0] || watchOption?.value) {
203
199
  const dtsPathOption = popOption(
204
200
  "dts-path",
205
201
  "type-declaration-path",
@@ -208,11 +204,11 @@ switch (commands[0]) {
208
204
  "gen-types"
209
205
  )
210
206
  dtsPathOption &&
211
- "emit-dts" != dtsPathOption.name &&
212
- "gen-dts" != dtsPathOption.name &&
207
+ "dts-path" != dtsPathOption.name &&
208
+ "type-declaration-path" != dtsPathOption.name &&
213
209
  console.warn(
214
210
  colourF(
215
- `Warning: ${colourN(dtsPathOption.name)} is being 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`
211
+ `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
212
  )
217
213
  )
218
214
  complainAboutUnrecognisedOptions()
@@ -224,9 +220,54 @@ switch (commands[0]) {
224
220
  minify: noMinifyOption && !noMinifyOption.value,
225
221
  mangleNames: mangleNamesOption?.value,
226
222
  onReady: () => log("Watching"),
227
- forceQuineCheats: forceQuineCheatsOption?.value
223
+ forceQuineCheats: forceQuineCheatsOption?.value ?? !noQuineCheatsOptions?.value
228
224
  })
229
225
  autoExit = !1
226
+ } else {
227
+ const dtsPathOption = popOption("dts-path")
228
+ complainAboutUnrecognisedOptions()
229
+ let declarationPathPromise
230
+ if (dtsPathOption) {
231
+ if ("string" != typeof dtsPathOption.value) {
232
+ logError(
233
+ `Option ${formatOption(dtsPathOption.name)} must be a string, got ${colourV(dtsPathOption.value)}\n`
234
+ )
235
+ logHelp()
236
+ process.exit(1)
237
+ }
238
+ let typeDeclarationPath = resolve(dtsPathOption.value)
239
+ const typeDeclaration = await generateTypeDeclaration(sourcePath, hackmudPath)
240
+ declarationPathPromise = writeFile(typeDeclarationPath, typeDeclaration)
241
+ .catch(error => {
242
+ assert(error instanceof Error, "src/bin/hsm.ts:288:38")
243
+ if ("EISDIR" != error.code) throw error
244
+ typeDeclarationPath = resolve(typeDeclarationPath, "player.d.ts")
245
+ return writeFile(typeDeclarationPath, typeDeclaration)
246
+ })
247
+ .then(() => typeDeclarationPath)
248
+ }
249
+ const { push, MissingSourceFolderError, MissingHackmudFolderError, NoUsersError } =
250
+ await pushModule,
251
+ infos = await push(sourcePath, hackmudPath, {
252
+ scripts,
253
+ onPush: info => logInfo(info, hackmudPath),
254
+ minify: noMinifyOption && !noMinifyOption.value,
255
+ mangleNames: mangleNamesOption?.value,
256
+ forceQuineCheats: forceQuineCheatsOption?.value ?? !noQuineCheatsOptions?.value
257
+ })
258
+ if (infos instanceof Error) {
259
+ logError(infos.message)
260
+ if (infos instanceof MissingSourceFolderError || infos instanceof NoUsersError) {
261
+ console.log()
262
+ logHelp()
263
+ } else
264
+ infos instanceof MissingHackmudFolderError &&
265
+ log(
266
+ `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`
267
+ )
268
+ } else infos.length || logError("Could not find any scripts to push")
269
+ declarationPathPromise &&
270
+ log("Wrote type declaration to " + chalk.bold(await declarationPathPromise))
230
271
  }
231
272
  }
232
273
  }
@@ -262,13 +303,14 @@ switch (commands[0]) {
262
303
  case "gen-types":
263
304
  case "emit-dts":
264
305
  {
265
- "emit-dts" != commands[0] &&
266
- "gen-dts" != commands[0] &&
306
+ if ("emit-dts" != commands[0] && "gen-dts" != commands[0]) {
307
+ warnedDeprecatedEmitDtsAlias = !0
267
308
  console.warn(
268
309
  colourF(
269
- `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`
310
+ `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`
270
311
  )
271
312
  )
313
+ }
272
314
  const hackmudPath = getHackmudPath(),
273
315
  target = commands[1]
274
316
  if (!target) {
@@ -282,7 +324,7 @@ switch (commands[0]) {
282
324
  typeDeclaration = await generateTypeDeclaration(sourcePath, hackmudPath)
283
325
  let typeDeclarationPath = resolve(outputPath)
284
326
  await writeFile(typeDeclarationPath, typeDeclaration).catch(error => {
285
- assert(error instanceof Error, "src/bin/hsm.ts:361:35")
327
+ assert(error instanceof Error, "src/bin/hsm.ts:422:35")
286
328
  if ("EISDIR" != error.code) throw error
287
329
  typeDeclarationPath = resolve(typeDeclarationPath, "player.d.ts")
288
330
  return writeFile(typeDeclarationPath, typeDeclaration)
@@ -300,7 +342,6 @@ switch (commands[0]) {
300
342
  autoExit && process.exit()
301
343
  function logHelp() {
302
344
  const pushCommandDescription = "Push scripts from a directory to hackmud user's scripts directories",
303
- forceQuineCheatsOptionDescription = `Force quine cheats on. Use ${colourN("--force-quine-cheats")}=${colourV("false")} to force off`,
304
345
  hackmudPathOption = `${colourN("--hackmud-path")}=${colourB("<path>")}\n Override hackmud path`
305
346
  switch (commands[0]) {
306
347
  case "dev":
@@ -308,7 +349,7 @@ function logHelp() {
308
349
  case "push":
309
350
  console.log(
310
351
  colourS(
311
- `${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("--dts-path")}=${colourB("<path>")}\n Path to generate a type declaration (.d.ts) 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`
352
+ `${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\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`
312
353
  )
313
354
  )
314
355
  break
@@ -323,7 +364,7 @@ function logHelp() {
323
364
  case "golf":
324
365
  console.log(
325
366
  colourS(
326
- `${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`
367
+ `${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`
327
368
  )
328
369
  )
329
370
  break
@@ -332,11 +373,12 @@ function logHelp() {
332
373
  case "gen-dts":
333
374
  case "gen-types":
334
375
  case "emit-dts":
335
- "emit-dts" != commands[0] &&
336
- "gen-dts" != commands[0] &&
376
+ warnedDeprecatedEmitDtsAlias ||
377
+ "emit-dts" == commands[0] ||
378
+ "gen-dts" == commands[0] ||
337
379
  console.warn(
338
380
  colourF(
339
- `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`
381
+ `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`
340
382
  )
341
383
  )
342
384
  console.log(
@@ -355,35 +397,49 @@ function logHelp() {
355
397
  default:
356
398
  console.log(
357
399
  colourS(
358
- `${colourJ("Hackmud Script Manager")}\n${colourN("Version") + colourS(": ") + colourV("0.20.4-a7d8309")}\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\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`
400
+ `${colourJ("Hackmud Script Manager")}\n${colourN("Version") + colourS(": ") + colourV("0.20.4-ae3052c")}\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`
359
401
  )
360
402
  )
361
403
  }
362
404
  }
363
- function logInfo({ path, users, characterCount, error }, hackmudPath) {
405
+ function logInfo({ path, users, characterCount, error, warnings }, hackmudPath) {
364
406
  path = relative(".", path)
365
- error ?
366
- logError(`Error "${chalk.bold(error.message)}" in ${chalk.bold(path)}`)
367
- : log(
407
+ if (error) logError(`Error "${chalk.bold(error.message)}" in ${chalk.bold(path)}`)
408
+ else {
409
+ for (const warning of warnings) console.warn(colourF("Warning: " + warning.message))
410
+ log(
368
411
  `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")}`
369
412
  )
413
+ }
370
414
  }
371
415
  function logError(message) {
372
416
  console.error(colourD(message))
373
417
  process.exitCode = 1
374
418
  }
375
419
  function getHackmudPath() {
376
- const hackmudPathOption = popOption("hackmud-path")?.value
377
- if (null != hackmudPathOption && "string" != typeof hackmudPathOption) {
378
- logError(`Option ${colourN("--hackmud-path")} must be a string, got ${colourV(hackmudPathOption)}\n`)
379
- logHelp()
380
- process.exit(1)
420
+ const hackmudPathOption = popOption("hackmud-path")
421
+ if (hackmudPathOption) {
422
+ if ("string" != typeof hackmudPathOption.value) {
423
+ logError(`Option ${colourN("--hackmud-path")} must be a string, got ${colourV(hackmudPathOption.value)}\n`)
424
+ logHelp()
425
+ process.exit(1)
426
+ }
427
+ if (!hackmudPathOption.value) {
428
+ logError(`Option ${colourN("--hackmud-path")} was specified but empty\n`)
429
+ logHelp()
430
+ process.exit(1)
431
+ }
432
+ return hackmudPathOption.value
381
433
  }
382
- return (
383
- hackmudPathOption ||
384
- process.env.HSM_HACKMUD_PATH ||
385
- ("win32" == process.platform ? resolve(process.env.APPDATA, "hackmud") : resolve(homedir(), ".config/hackmud"))
386
- )
434
+ if (null != process.env.HSM_HACKMUD_PATH) {
435
+ if (!process.env.HSM_HACKMUD_PATH) {
436
+ logError(`Environment variable ${colourN("HSM_HACKMUD_PATH")} was specified but empty\n`)
437
+ logHelp()
438
+ process.exit(1)
439
+ }
440
+ return process.env.HSM_HACKMUD_PATH
441
+ }
442
+ return "win32" == process.platform ? resolve(process.env.APPDATA, "hackmud") : resolve(homedir(), ".config/hackmud")
387
443
  }
388
444
  function assertOptionIsBoolean(option) {
389
445
  if ("boolean" != typeof option.value) {
@@ -395,16 +451,15 @@ function assertOptionIsBoolean(option) {
395
451
  function popOption(...names) {
396
452
  const presentOptionNames = names.filter(name => options.has(name))
397
453
  if (!presentOptionNames.length) return
398
- const presentOptionNamesWithDashDash = presentOptionNames.map(formatOption)
399
454
  if (presentOptionNames.length > 1) {
400
455
  logError(
401
- `The options ${presentOptionNamesWithDashDash.join(", ")} are aliases for each other. Please only specify one`
456
+ `The options ${presentOptionNames.map(formatOption).join(", ")} are aliases for each other. Please only specify one`
402
457
  )
403
458
  process.exit(1)
404
459
  }
405
460
  const value = options.get(presentOptionNames[0])
406
461
  options.delete(presentOptionNames[0])
407
- return { name: presentOptionNamesWithDashDash[0], value }
462
+ return { name: presentOptionNames[0], value }
408
463
  }
409
464
  function complainAboutUnrecognisedOptions() {
410
465
  if (options.size) {
package/env.d.ts CHANGED
@@ -1046,6 +1046,9 @@ declare global {
1046
1046
  * In rare cases where it's not known at build time, it's `"UNKNOWN"`. */
1047
1047
  const _SCRIPT_USER: string
1048
1048
 
1049
+ /** @deprecated Use `_SCRIPT_SUBNAME` instead. */
1050
+ const _SCRIPT_NAME: string
1051
+
1049
1052
  /** The name of this script excluding the user and `.`.
1050
1053
  *
1051
1054
  * e.g. in the script `foo.bar`, `_SCRIPT_NAME` is `bar`.
@@ -1053,7 +1056,7 @@ declare global {
1053
1056
  * Shorter alternative to `context.this_script.split(".")[1].
1054
1057
  *
1055
1058
  * In rare cases where it's not known at build time, it's `"UNKNOWN"`. */
1056
- const _SCRIPT_NAME: string
1059
+ const _SCRIPT_SUBNAME: string
1057
1060
 
1058
1061
  /** The full name of this script equivilent to `context.this_script` but should use less characters.
1059
1062
  *
package/index.d.ts CHANGED
@@ -10,4 +10,7 @@ export type Info = {
10
10
  users: string[];
11
11
  characterCount: number;
12
12
  error: Error | undefined;
13
+ warnings: {
14
+ message: string;
15
+ }[];
13
16
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hackmud-script-manager",
3
- "version": "0.20.4-a7d8309",
3
+ "version": "0.20.4-ae3052c",
4
4
  "description": "Script manager for game hackmud, with minification, TypeScript support, and player script type definition generation.",
5
5
  "keywords": [
6
6
  "api",
@@ -26,6 +26,5 @@ export declare function processScript(code: string, { minify: shouldMinify, uniq
26
26
  script: string;
27
27
  warnings: {
28
28
  message: string;
29
- line: number;
30
29
  }[];
31
30
  }>;
@@ -234,7 +234,7 @@ async function processScript(
234
234
  }),
235
235
  seclevelNames = ["NULLSEC", "LOWSEC", "MIDSEC", "HIGHSEC", "FULLSEC"]
236
236
  code = (await bundle.generate({})).output[0].code
237
- const { file, seclevel } = transform(parse(code, { sourceType: "module" }), sourceCode, {
237
+ const { file, seclevel, warnings } = transform(parse(code, { sourceType: "module" }), sourceCode, {
238
238
  uniqueId,
239
239
  scriptUser,
240
240
  scriptName
@@ -328,6 +328,6 @@ async function processScript(
328
328
  throw Error(
329
329
  'you found a weird edge case where I wasn\'t able to replace illegal strings like "SC$", please report thx'
330
330
  )
331
- return { script: code, warnings: [] }
331
+ return { script: code, warnings }
332
332
  }
333
333
  export { minify, postprocess, preprocess, processScript, transform }
@@ -16,4 +16,7 @@ export type TransformOptions = LaxPartial<{
16
16
  export declare function transform(file: File, sourceCode: string, { uniqueId, scriptUser, scriptName, seclevel }: TransformOptions): {
17
17
  file: File;
18
18
  seclevel: number;
19
+ warnings: {
20
+ message: string;
21
+ }[];
19
22
  };
@@ -22,7 +22,8 @@ const { default: traverse } = babelTraverse,
22
22
  "BigInt"
23
23
  ]
24
24
  function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scriptName, seclevel = 4 }) {
25
- const topFunctionName = `_${uniqueId}_SCRIPT_`
25
+ const warnings = [],
26
+ topFunctionName = `_${uniqueId}_SCRIPT_`
26
27
  let program
27
28
  traverse(file, {
28
29
  Program(path) {
@@ -44,9 +45,17 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
44
45
  referencePath.replaceWith(t.identifier(`_${uniqueId}_SCRIPT_USER_`))
45
46
  } else
46
47
  referencePath.replaceWith(t.stringLiteral(1 == scriptUser ? `$${uniqueId}$SCRIPT_USER$` : scriptUser))
47
- if (program.scope.hasGlobal("_SCRIPT_NAME"))
48
+ if (program.scope.hasGlobal("_SCRIPT_NAME")) {
49
+ warnings.push({
50
+ message:
51
+ "Global _SCRIPT_NAME is deprecated and will be removed in the next minor release of HSM, use _SCRIPT_SUBNAME instead"
52
+ })
48
53
  for (const referencePath of getReferencePathsToGlobal("_SCRIPT_NAME", program))
49
54
  referencePath.replaceWith(t.stringLiteral(1 == scriptName ? `$${uniqueId}$SCRIPT_NAME$` : scriptName))
55
+ }
56
+ if (program.scope.hasGlobal("_SCRIPT_SUBNAME"))
57
+ for (const referencePath of getReferencePathsToGlobal("_SCRIPT_SUBNAME", program))
58
+ referencePath.replaceWith(t.stringLiteral(1 == scriptName ? `$${uniqueId}$SCRIPT_NAME$` : scriptName))
50
59
  if (program.scope.hasGlobal("_FULL_SCRIPT_NAME"))
51
60
  for (const referencePath of getReferencePathsToGlobal("_FULL_SCRIPT_NAME", program))
52
61
  if (1 == scriptUser || 1 == scriptName)
@@ -68,30 +77,30 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
68
77
  const referencePath = FunctionReferencePaths[0]
69
78
  assert(
70
79
  "MemberExpression" == referencePath.parent.type,
71
- "src/processScript/transform.ts:103:8 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
80
+ "src/processScript/transform.ts:111:8 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
72
81
  )
73
82
  assert(
74
83
  "Identifier" == referencePath.parent.property.type,
75
- "src/processScript/transform.ts:108:8 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
84
+ "src/processScript/transform.ts:116:8 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
76
85
  )
77
86
  assert(
78
87
  "prototype" == referencePath.parent.property.name,
79
- "src/processScript/transform.ts:113:8 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
88
+ "src/processScript/transform.ts:121:8 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
80
89
  )
81
90
  referencePath.parentPath.replaceWith(createGetFunctionPrototypeNode())
82
91
  } else {
83
92
  for (const referencePath of FunctionReferencePaths) {
84
93
  assert(
85
94
  "MemberExpression" == referencePath.parent.type,
86
- "src/processScript/transform.ts:121:9 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
95
+ "src/processScript/transform.ts:129:9 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
87
96
  )
88
97
  assert(
89
98
  "Identifier" == referencePath.parent.property.type,
90
- "src/processScript/transform.ts:126:9 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
99
+ "src/processScript/transform.ts:134:9 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
91
100
  )
92
101
  assert(
93
102
  "prototype" == referencePath.parent.property.name,
94
- "src/processScript/transform.ts:131:9 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
103
+ "src/processScript/transform.ts:139:9 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
95
104
  )
96
105
  functionDotPrototypeIsReferencedMultipleTimes = !0
97
106
  referencePath.parentPath.replaceWith(t.identifier(`_${uniqueId}_FUNCTION_DOT_PROTOTYPE_`))
@@ -127,12 +136,12 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
127
136
  const neededDbMethodLets = new Set()
128
137
  if (program.scope.hasGlobal("$db"))
129
138
  for (const referencePath of getReferencePathsToGlobal("$db", program)) {
130
- assert("MemberExpression" == referencePath.parentPath.node.type, "src/processScript/transform.ts:185:69")
131
- assert("Identifier" == referencePath.parentPath.node.property.type, "src/processScript/transform.ts:186:72")
139
+ assert("MemberExpression" == referencePath.parentPath.node.type, "src/processScript/transform.ts:193:69")
140
+ assert("Identifier" == referencePath.parentPath.node.property.type, "src/processScript/transform.ts:194:72")
132
141
  const databaseOpMethodName = referencePath.parentPath.node.property.name
133
142
  assert(
134
143
  validDBMethods.includes(databaseOpMethodName),
135
- `src/processScript/transform.ts:192:8 invalid db method "${databaseOpMethodName}", valid db methods are "${validDBMethods.join('", "')}"`
144
+ `src/processScript/transform.ts:200:8 invalid db method "${databaseOpMethodName}", valid db methods are "${validDBMethods.join('", "')}"`
136
145
  )
137
146
  if ("CallExpression" == referencePath.parentPath.parentPath?.type)
138
147
  referencePath.parentPath.replaceWith(t.identifier(`$${uniqueId}$DB$${databaseOpMethodName}$`))
@@ -167,7 +176,7 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
167
176
  if (program.scope.hasGlobal("Object"))
168
177
  for (const referencePath of getReferencePathsToGlobal("Object", program))
169
178
  if ("MemberExpression" == referencePath.parent.type && !referencePath.parent.computed) {
170
- assert("Identifier" == referencePath.parent.property.type, "src/processScript/transform.ts:242:64")
179
+ assert("Identifier" == referencePath.parent.property.type, "src/processScript/transform.ts:250:64")
171
180
  if ("getPrototypeOf" == referencePath.parent.property.name) {
172
181
  referencePath.parentPath.replaceWith(t.identifier(`_${uniqueId}_GET_PROTOTYPE_OF_`))
173
182
  needGetPrototypeOf = !0
@@ -180,7 +189,7 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
180
189
  if (program.scope.hasGlobal("console"))
181
190
  for (const referencePath of getReferencePathsToGlobal("console", program))
182
191
  if ("MemberExpression" == referencePath.parent.type && !referencePath.parent.computed) {
183
- assert("Identifier" == referencePath.parent.property.type, "src/processScript/transform.ts:260:64")
192
+ assert("Identifier" == referencePath.parent.property.type, "src/processScript/transform.ts:268:64")
184
193
  referencePath.parentPath.replaceWith(
185
194
  t.identifier(`_${uniqueId}_CONSOLE_METHOD_${referencePath.parent.property.name}_`)
186
195
  )
@@ -188,13 +197,13 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
188
197
  }
189
198
  const lastStatement = program.node.body.at(-1)
190
199
  let exportDefaultName
191
- assert(lastStatement, "src/processScript/transform.ts:274:27 program is empty")
200
+ assert(lastStatement, "src/processScript/transform.ts:282:27 program is empty")
192
201
  if ("ExportNamedDeclaration" == lastStatement.type) {
193
202
  program.node.body.pop()
194
203
  for (const specifier of lastStatement.specifiers) {
195
204
  assert(
196
205
  "ExportSpecifier" == specifier.type,
197
- `src/processScript/transform.ts:280:51 ${specifier.type} is currently unsupported`
206
+ `src/processScript/transform.ts:288:51 ${specifier.type} is currently unsupported`
198
207
  )
199
208
  if (
200
209
  "default" !=
@@ -296,11 +305,11 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
296
305
  let hoistedGlobalBlockFunctions = 0
297
306
  for (const [globalBlockIndex, globalBlockStatement] of [...globalBlock.body.entries()].reverse())
298
307
  if ("VariableDeclaration" == globalBlockStatement.type) {
299
- assert(1 == globalBlockStatement.declarations.length, "src/processScript/transform.ts:394:59")
308
+ assert(1 == globalBlockStatement.declarations.length, "src/processScript/transform.ts:402:59")
300
309
  const declarator = globalBlockStatement.declarations[0]
301
310
  assert(
302
311
  "Identifier" == declarator.id.type,
303
- `src/processScript/transform.ts:398:51 declarator.id.type was "${declarator.id.type}"`
312
+ `src/processScript/transform.ts:406:51 declarator.id.type was "${declarator.id.type}"`
304
313
  )
305
314
  program.scope.crawl()
306
315
  if (program.scope.hasGlobal(declarator.id.name)) {
@@ -315,9 +324,9 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
315
324
  Object.keys(program.scope.globals).some(global => globalBlockVariables.has(global))
316
325
  ) {
317
326
  const binding = program.scope.getBinding(declarator.id.name)
318
- assert(binding, "src/processScript/transform.ts:417:23")
327
+ assert(binding, "src/processScript/transform.ts:425:23")
319
328
  for (const referencePath of binding.referencePaths) {
320
- assert("Identifier" == referencePath.node.type, "src/processScript/transform.ts:420:56")
329
+ assert("Identifier" == referencePath.node.type, "src/processScript/transform.ts:428:56")
321
330
  referencePath.replaceWith(
322
331
  t.memberExpression(
323
332
  t.identifier(`_${uniqueId}_G_`),
@@ -365,16 +374,16 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
365
374
  } else globalBlockVariables.add(declarator.id.name)
366
375
  } else if ("ClassDeclaration" == globalBlockStatement.type) {
367
376
  program.scope.crawl()
368
- assert(globalBlockStatement.id, "src/processScript/transform.ts:477:37")
377
+ assert(globalBlockStatement.id, "src/processScript/transform.ts:485:37")
369
378
  if (program.scope.hasGlobal(globalBlockStatement.id.name)) {
370
379
  globalBlock.body.splice(globalBlockIndex, 1)
371
380
  const [globalBlockPath] = program.unshiftContainer("body", globalBlock),
372
381
  [globalBlockStatementPath] = program.unshiftContainer("body", globalBlockStatement)
373
382
  program.scope.crawl()
374
383
  const binding = program.scope.getBinding(globalBlockStatement.id.name)
375
- assert(binding, "src/processScript/transform.ts:489:22")
384
+ assert(binding, "src/processScript/transform.ts:497:22")
376
385
  for (const referencePath of binding.referencePaths) {
377
- assert("Identifier" == referencePath.node.type, "src/processScript/transform.ts:492:55")
386
+ assert("Identifier" == referencePath.node.type, "src/processScript/transform.ts:500:55")
378
387
  referencePath.replaceWith(
379
388
  t.memberExpression(t.identifier(`_${uniqueId}_G_`), t.identifier(referencePath.node.name))
380
389
  )
@@ -566,7 +575,7 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
566
575
  }
567
576
  },
568
577
  ClassBody({ node: classBody, scope, parent }) {
569
- assert(t.isClass(parent), "src/processScript/transform.ts:687:30")
578
+ assert(t.isClass(parent), "src/processScript/transform.ts:695:30")
570
579
  let thisIsReferenced = !1
571
580
  for (const classMethod of classBody.body) {
572
581
  if ("ClassMethod" != classMethod.type) continue
@@ -656,7 +665,7 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
656
665
  )
657
666
  }
658
667
  })
659
- return { file, seclevel }
668
+ return { file, seclevel, warnings }
660
669
  function createGetFunctionPrototypeNode() {
661
670
  const name = globalFunctionsUnder7Characters.find(name => !program.scope.hasOwnBinding(name))
662
671
  return t.memberExpression(
@@ -666,23 +675,23 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
666
675
  }
667
676
  function processFakeSubscriptObject(fakeSubscriptObjectName, seclevel) {
668
677
  for (const referencePath of getReferencePathsToGlobal(fakeSubscriptObjectName, program)) {
669
- assert("MemberExpression" == referencePath.parent.type, "src/processScript/transform.ts:793:60")
678
+ assert("MemberExpression" == referencePath.parent.type, "src/processScript/transform.ts:801:60")
670
679
  assert("Identifier" == referencePath.parent.property.type)
671
680
  assert(
672
681
  "MemberExpression" == referencePath.parentPath.parentPath?.node.type,
673
- "src/processScript/transform.ts:795:81"
682
+ "src/processScript/transform.ts:803:81"
674
683
  )
675
684
  assert(
676
685
  "Identifier" == referencePath.parentPath.parentPath.node.property.type,
677
- "src/processScript/transform.ts:796:83"
686
+ "src/processScript/transform.ts:804:83"
678
687
  )
679
688
  assert(
680
689
  /^[_a-z][\d_a-z]{0,24}$/.test(referencePath.parent.property.name),
681
- `src/processScript/transform.ts:800:8 invalid user "${referencePath.parent.property.name}" in subscript`
690
+ `src/processScript/transform.ts:808:8 invalid user "${referencePath.parent.property.name}" in subscript`
682
691
  )
683
692
  assert(
684
693
  /^[_a-z][\d_a-z]{0,24}$/.test(referencePath.parentPath.parentPath.node.property.name),
685
- `src/processScript/transform.ts:805:8 invalid script name "${referencePath.parentPath.parentPath.node.property.name}" in subscript`
694
+ `src/processScript/transform.ts:813:8 invalid script name "${referencePath.parentPath.parentPath.node.property.name}" in subscript`
686
695
  )
687
696
  if ("CallExpression" == referencePath.parentPath.parentPath.parentPath?.type)
688
697
  referencePath.parentPath.parentPath.replaceWith(
package/push.js CHANGED
@@ -131,7 +131,7 @@ async function push(
131
131
  uniqueId = Math.floor(Math.random() * 2 ** 52)
132
132
  .toString(36)
133
133
  .padStart(11, "0"),
134
- { script: minifiedCode } = await processScript(await readFile(path, { encoding: "utf8" }), {
134
+ { script: minifiedCode, warnings } = await processScript(await readFile(path, { encoding: "utf8" }), {
135
135
  minify,
136
136
  scriptUser: !0,
137
137
  scriptName,
@@ -140,7 +140,7 @@ async function push(
140
140
  mangleNames,
141
141
  forceQuineCheats
142
142
  }),
143
- info = { path, users, characterCount: countHackmudCharacters(minifiedCode), error: void 0 }
143
+ info = { path, users, characterCount: countHackmudCharacters(minifiedCode), error: void 0, warnings }
144
144
  await Promise.all(
145
145
  users.map(user =>
146
146
  writeFilePersistent(
package/watch.js CHANGED
@@ -73,10 +73,11 @@ async function watch(
73
73
  : scriptName && "*" != scriptName ? wildUserScripts.add(scriptName)
74
74
  : (pushEverything = !0)
75
75
  }
76
- const watcher = watch$1(["*.ts", "*.js", "*/*.ts", "*/*.js"], {
76
+ const watcher = watch$1(".", {
77
77
  cwd: sourceDirectory,
78
78
  awaitWriteFinish: { stabilityThreshold: 100 },
79
- ignored: "*.d.ts"
79
+ ignored: (path, stats) =>
80
+ !!stats?.isFile() && !(path.endsWith(".js") || (path.endsWith(".ts") && !path.endsWith(".d.ts")))
80
81
  }).on("change", async path => {
81
82
  if (path.endsWith(".d.ts")) return
82
83
  const extension = extname(path)
@@ -118,27 +119,22 @@ async function watch(
118
119
  for (const user of scriptNamesToUsers.get(scriptName)) usersToPushToSet.add(user)
119
120
  const usersToPushTo = [...usersToPushToSet].filter(user => !scriptNamesToUsersToSkip.has(user))
120
121
  if (!usersToPushTo.length) {
121
- onPush?.({ path, users: [], characterCount: 0, error: Error("no users to push to") })
122
+ onPush?.({ path, users: [], characterCount: 0, error: Error("no users to push to"), warnings: [] })
122
123
  return
123
124
  }
124
125
  const uniqueId = Math.floor(Math.random() * 2 ** 52)
125
126
  .toString(36)
126
127
  .padStart(11, "0"),
127
128
  filePath = resolve(sourceDirectory, path)
128
- let minifiedCode
129
+ let minifiedCode, warnings
129
130
  try {
130
- ;({ script: minifiedCode } = await processScript(await readFile(filePath, { encoding: "utf8" }), {
131
- minify,
132
- scriptUser: !0,
133
- scriptName,
134
- uniqueId,
135
- filePath,
136
- mangleNames,
137
- forceQuineCheats
138
- }))
131
+ ;({ script: minifiedCode, warnings } = await processScript(
132
+ await readFile(filePath, { encoding: "utf8" }),
133
+ { minify, scriptUser: !0, scriptName, uniqueId, filePath, mangleNames, forceQuineCheats }
134
+ ))
139
135
  } catch (error) {
140
- assert(error instanceof Error, "src/watch.ts:146:36")
141
- onPush?.({ path, users: [], characterCount: 0, error })
136
+ assert(error instanceof Error, "src/watch.ts:149:36")
137
+ onPush?.({ path, users: [], characterCount: 0, error, warnings: [] })
142
138
  return
143
139
  }
144
140
  await Promise.all(
@@ -155,7 +151,8 @@ async function watch(
155
151
  path,
156
152
  users: usersToPushTo,
157
153
  characterCount: countHackmudCharacters(minifiedCode),
158
- error: void 0
154
+ error: void 0,
155
+ warnings
159
156
  })
160
157
  return
161
158
  }
@@ -171,9 +168,9 @@ async function watch(
171
168
  return
172
169
  const filePath = resolve(sourceDirectory, path),
173
170
  sourceCode = await readFile(filePath, { encoding: "utf8" })
174
- let script
171
+ let script, warnings
175
172
  try {
176
- ;({ script } = await processScript(sourceCode, {
173
+ ;({ script, warnings } = await processScript(sourceCode, {
177
174
  minify,
178
175
  scriptUser: user,
179
176
  scriptName,
@@ -182,12 +179,12 @@ async function watch(
182
179
  forceQuineCheats
183
180
  }))
184
181
  } catch (error) {
185
- assert(error instanceof Error, "src/watch.ts:182:35")
186
- onPush?.({ path, users: [], characterCount: 0, error })
182
+ assert(error instanceof Error, "src/watch.ts:190:35")
183
+ onPush?.({ path, users: [], characterCount: 0, error, warnings: [] })
187
184
  return
188
185
  }
189
186
  await writeFilePersistent(resolve(hackmudDirectory, user, "scripts", scriptName + ".js"), script)
190
- onPush?.({ path, users: [user], characterCount: countHackmudCharacters(script), error: void 0 })
187
+ onPush?.({ path, users: [user], characterCount: countHackmudCharacters(script), error: void 0, warnings })
191
188
  })
192
189
  onReady && watcher.on("ready", onReady)
193
190
  if (!typeDeclarationPath_) return
@@ -197,7 +194,7 @@ async function watch(
197
194
  try {
198
195
  await writeFile(typeDeclarationPath, typeDeclaration)
199
196
  } catch (error) {
200
- assert(error instanceof Error, "src/watch.ts:215:35")
197
+ assert(error instanceof Error, "src/watch.ts:223:35")
201
198
  if ("EISDIR" != error.code) throw error
202
199
  typeDeclarationPath = resolve(typeDeclarationPath, "player.d.ts")
203
200
  await writeFile(typeDeclarationPath, typeDeclaration)