hackmud-script-manager 0.20.4-d2fc4af → 0.20.4-d4996d7

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
@@ -5,14 +5,16 @@ 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-d2fc4af",
16
+ const version = "0.20.4-d4996d7",
17
+ formatOption = name => colourN(`-${1 == name.length ? "" : "-"}${name}`),
16
18
  options = new Map(),
17
19
  commands = [],
18
20
  userColours = new Cache(user => {
@@ -36,10 +38,6 @@ for (const argument of process.argv.slice(2))
36
38
  if ("-" == argument[1]) options.set(key.slice(2), value)
37
39
  else for (const option of key.slice(1)) options.set(option, value)
38
40
  } 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
41
  const pushModule = import("../push.js"),
44
42
  processScriptModule = import("../processScript/index.js"),
45
43
  watchModule = import("../watch.js"),
@@ -49,6 +47,7 @@ const pushModule = import("../push.js"),
49
47
  colourB = chalk.rgb(202, 202, 202),
50
48
  colourC = chalk.rgb(155, 155, 155),
51
49
  colourD = chalk.rgb(255, 0, 0),
50
+ colourF = chalk.rgb(255, 128, 0),
52
51
  colourJ = chalk.rgb(255, 244, 4),
53
52
  colourK = chalk.rgb(243, 249, 152),
54
53
  colourL = chalk.rgb(30, 255, 0),
@@ -57,162 +56,171 @@ const pushModule = import("../push.js"),
57
56
  colourS = chalk.rgb(122, 178, 244),
58
57
  colourV = chalk.rgb(255, 0, 236),
59
58
  colourW = chalk.rgb(255, 150, 224)
60
- if (options.get("help") || options.get("h")) {
59
+ process.version.startsWith("v21.") &&
60
+ console.warn(
61
+ colourF(
62
+ "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"
63
+ )
64
+ )
65
+ if ("v" == commands[0] || "version" == commands[0] || popOption("version", "v")?.value) {
66
+ console.log(version)
67
+ process.exit()
68
+ }
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")) {
81
+ const noMinifyOption = popOption("no-minify", "skip-minify"),
82
+ mangleNamesOption = popOption("mangle-names"),
83
+ forceQuineCheatsOption = popOption("force-quine-cheats"),
84
+ noMinifyIncompatibleOption = mangleNamesOption || forceQuineCheatsOption
85
+ if (noMinifyOption && noMinifyIncompatibleOption) {
88
86
  logError(
89
- `Options ${colourN("--mangle-names")} and ${colourN(optionsHasNoMinify ? "--no-minify" : "--skip-minify")} are incompatible\n`
87
+ `Options ${formatOption(noMinifyOption.name)} and ${formatOption(noMinifyIncompatibleOption.name)} are incompatible\n`
90
88
  )
91
89
  logHelp()
92
- break
90
+ process.exit(1)
93
91
  }
94
- const shouldSkipMinify = options.get("no-minify") || options.get("skip-minify")
95
- let shouldMinify
96
- if (null != shouldSkipMinify) {
97
- if ("boolean" != typeof shouldSkipMinify) {
92
+ noMinifyOption && assertOptionIsBoolean(noMinifyOption)
93
+ mangleNamesOption && assertOptionIsBoolean(mangleNamesOption)
94
+ forceQuineCheatsOption && assertOptionIsBoolean(forceQuineCheatsOption)
95
+ if ("golf" == commands[0] || "minify" == commands[0]) {
96
+ const watchOption = popOption("watch"),
97
+ target = commands[1]
98
+ if (!target) {
99
+ logError("Must provide target\n")
100
+ logHelp()
101
+ process.exit(1)
102
+ }
103
+ const fileExtension = extname(target)
104
+ if (!supportedExtensions.includes(fileExtension)) {
98
105
  logError(
99
- `The value for ${colourN(optionsHasNoMinify ? "--no-minify" : "--skip-minify")} must be ${colourV("true")} or ${colourV("false")}\n`
106
+ `Unsupported file extension "${chalk.bold(fileExtension)}"\nSupported extensions are "${supportedExtensions.map(extension => chalk.bold(extension)).join('", "')}"`
100
107
  )
101
- logHelp()
102
- break
108
+ process.exit(1)
103
109
  }
104
- shouldMinify = !shouldSkipMinify
105
- }
106
- const shouldMangleNames = options.get("mangle-names")
107
- if (null != shouldMangleNames && "boolean" != typeof shouldMangleNames) {
108
- logError(
109
- `The value for ${colourN("--mangle-names")} must be ${colourV("true")} or ${colourV("false")}\n`
110
- )
111
- logHelp()
112
- break
113
- }
114
- const shouldforceQuineCheats = options.get("force-quine-cheats")
115
- if (null != shouldforceQuineCheats && "boolean" != typeof shouldforceQuineCheats) {
116
- logError(
117
- `The value for ${colourN("--force-quine-cheats")} must be ${colourV("true")} or ${colourV("false")}\n`
118
- )
119
- logHelp()
120
- break
121
- }
122
- const { push, MissingSourceFolderError, MissingHackmudFolderError, NoUsersError } = await pushModule,
123
- infos = await push(sourcePath, hackmudPath, {
124
- scripts,
125
- onPush: info => logInfo(info, hackmudPath),
126
- minify: shouldMinify,
127
- mangleNames: shouldMangleNames,
128
- forceQuineCheats: shouldforceQuineCheats
129
- })
130
- if (infos instanceof Error) {
131
- logError(infos.message)
132
- if (infos instanceof MissingSourceFolderError || infos instanceof NoUsersError) {
133
- console.log()
134
- logHelp()
135
- } else
136
- infos instanceof MissingHackmudFolderError &&
137
- log(
138
- `If this is not where your hackmud folder is, you can specify it with the\n${colourN("--hackmud-path")}=${colourB("<path>")} option or ${colourN("HSM_HACKMUD_PATH")} environment variable`
139
- )
140
- } else infos.length || logError("Could not find any scripts to push")
141
- }
142
- break
143
- case "dev":
144
- case "watch":
145
- {
146
- const hackmudPath = getHackmudPath(),
147
- sourcePath = commands[1]
148
- if (!sourcePath) {
149
- logError("Must provide the directory to watch\n")
150
- logHelp()
151
- break
152
- }
153
- const scripts = commands.slice(2)
154
- if (scripts.length) {
155
- const invalidScript = scripts.find(
156
- script => !/^(?:[a-z_][a-z\d_]{0,24}|\*)\.(?:[a-z_][a-z\d_]{0,24}|\*)$/.test(script)
157
- )
158
- if (invalidScript) {
159
- logError(`Invalid script name: ${JSON.stringify(invalidScript)}\n`)
110
+ complainAboutUnrecognisedOptions()
111
+ const { processScript } = await processScriptModule,
112
+ fileBaseName = basename(target, fileExtension),
113
+ fileBaseNameEndsWithDotSrc = fileBaseName.endsWith(".src"),
114
+ scriptName = fileBaseNameEndsWithDotSrc ? fileBaseName.slice(0, -4) : fileBaseName,
115
+ scriptUser =
116
+ (
117
+ "scripts" == basename(resolve(target, "..")) &&
118
+ "hackmud" == basename(resolve(target, "../../.."))
119
+ ) ?
120
+ basename(resolve(target, "../.."))
121
+ : void 0
122
+ let outputPath =
123
+ commands[2] ||
124
+ resolve(
125
+ dirname(target),
126
+ fileBaseNameEndsWithDotSrc ? scriptName + ".js"
127
+ : ".js" == fileExtension ? fileBaseName + ".min.js"
128
+ : fileBaseName + ".js"
129
+ )
130
+ const golfFile = () =>
131
+ readFile(target, { encoding: "utf8" }).then(async source => {
132
+ const timeStart = performance.now(),
133
+ { script, warnings } = await processScript(source, {
134
+ minify: noMinifyOption && !noMinifyOption.value,
135
+ scriptUser,
136
+ scriptName,
137
+ filePath: target,
138
+ mangleNames: mangleNamesOption?.value,
139
+ forceQuineCheats: forceQuineCheatsOption?.value
140
+ }),
141
+ timeTook = performance.now() - timeStart
142
+ for (const { message, line } of warnings)
143
+ log(`Warning "${chalk.bold(message)}" on line ${chalk.bold(line + "")}`)
144
+ await writeFilePersistent(outputPath, script)
145
+ .catch(error => {
146
+ if (!commands[2] || "EISDIR" != error.code) throw error
147
+ outputPath = resolve(outputPath, basename(target, fileExtension) + ".js")
148
+ return writeFilePersistent(outputPath, script)
149
+ })
150
+ .then(() =>
151
+ log(
152
+ `Wrote ${chalk.bold(countHackmudCharacters(script))} chars to ${chalk.bold(relative(".", outputPath))} | took ${Math.round(100 * timeTook) / 100}ms`
153
+ )
154
+ )
155
+ })
156
+ if (watchOption) {
157
+ const { watch: watchFile } = await chokidarModule
158
+ watchFile(target, { awaitWriteFinish: { stabilityThreshold: 100 } })
159
+ .on("ready", () => log("Watching " + target))
160
+ .on("change", golfFile)
161
+ autoExit = !1
162
+ } else await golfFile()
163
+ } else {
164
+ const hackmudPath = getHackmudPath(),
165
+ sourcePath = commands[1]
166
+ if (!sourcePath) {
167
+ logError(`Must provide the directory to ${"push" == commands[0] ? "push from" : "watch"}\n`)
160
168
  logHelp()
161
- break
169
+ process.exit(1)
162
170
  }
163
- } else scripts.push("*.*")
164
- const optionsHasNoMinify = options.has("no-minify")
165
- if ((optionsHasNoMinify || options.has("skip-minify")) && options.has("mangle-names")) {
166
- logError(
167
- `Options ${colourN("--mangle-names")} and ${colourN(optionsHasNoMinify ? "--no-minify" : "--skip-minify")} are incompatible\n`
168
- )
169
- logHelp()
170
- break
171
- }
172
- const shouldSkipMinify = options.get("no-minify") || options.get("skip-minify")
173
- let shouldMinify
174
- if (null != shouldSkipMinify) {
175
- if ("boolean" != typeof shouldSkipMinify) {
176
- logError(
177
- `The value for ${colourN(optionsHasNoMinify ? "--no-minify" : "--skip-minify")} must be ${colourV("true")} or ${colourV("false")}\n`
171
+ const scripts = commands.slice(2)
172
+ if (scripts.length) {
173
+ const invalidScript = scripts.find(
174
+ script => !/^(?:[a-z_][a-z\d_]{0,24}|\*)\.(?:[a-z_][a-z\d_]{0,24}|\*)$/.test(script)
178
175
  )
179
- logHelp()
180
- break
176
+ if (invalidScript) {
177
+ logError(`Invalid script name: ${JSON.stringify(invalidScript)}\n`)
178
+ logHelp()
179
+ process.exit(1)
180
+ }
181
+ } else scripts.push("*.*")
182
+ if ("push" == commands[0]) {
183
+ const { push, MissingSourceFolderError, MissingHackmudFolderError, NoUsersError } = await pushModule
184
+ complainAboutUnrecognisedOptions()
185
+ const infos = await push(sourcePath, hackmudPath, {
186
+ scripts,
187
+ onPush: info => logInfo(info, hackmudPath),
188
+ minify: noMinifyOption && !noMinifyOption.value,
189
+ mangleNames: mangleNamesOption?.value,
190
+ forceQuineCheats: forceQuineCheatsOption?.value
191
+ })
192
+ if (infos instanceof Error) {
193
+ logError(infos.message)
194
+ if (infos instanceof MissingSourceFolderError || infos instanceof NoUsersError) {
195
+ console.log()
196
+ logHelp()
197
+ } else
198
+ infos instanceof MissingHackmudFolderError &&
199
+ log(
200
+ `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`
201
+ )
202
+ } else infos.length || logError("Could not find any scripts to push")
203
+ } else {
204
+ const typeDeclarationPathOption = popOption(
205
+ "type-declaration-path",
206
+ "type-declaration",
207
+ "dts",
208
+ "gen-types"
209
+ )
210
+ complainAboutUnrecognisedOptions()
211
+ const { watch } = await watchModule
212
+ watch(sourcePath, hackmudPath, {
213
+ scripts,
214
+ onPush: info => logInfo(info, hackmudPath),
215
+ typeDeclarationPath: typeDeclarationPathOption?.value.toString(),
216
+ minify: noMinifyOption && !noMinifyOption.value,
217
+ mangleNames: mangleNamesOption?.value,
218
+ onReady: () => log("Watching"),
219
+ forceQuineCheats: forceQuineCheatsOption?.value
220
+ })
221
+ autoExit = !1
181
222
  }
182
- shouldMinify = !shouldSkipMinify
183
223
  }
184
- const shouldMangleNames = options.get("mangle-names")
185
- if (null != shouldMangleNames && "boolean" != typeof shouldMangleNames) {
186
- logError(
187
- `The value for ${colourN("--mangle-names")} must be ${colourV("true")} or ${colourV("false")}\n`
188
- )
189
- logHelp()
190
- break
191
- }
192
- const shouldforceQuineCheats = options.get("force-quine-cheats")
193
- if (null != shouldforceQuineCheats && "boolean" != typeof shouldforceQuineCheats) {
194
- logError(
195
- `The value for ${colourN("--force-quine-cheats")} must be ${colourV("true")} or ${colourV("false")}\n`
196
- )
197
- logHelp()
198
- break
199
- }
200
- const { watch } = await watchModule
201
- watch(sourcePath, hackmudPath, {
202
- scripts,
203
- onPush: info => logInfo(info, hackmudPath),
204
- typeDeclarationPath: (
205
- options.get("type-declaration-path") ||
206
- options.get("type-declaration") ||
207
- options.get("dts") ||
208
- options.get("gen-types")
209
- )?.toString(),
210
- minify: shouldMinify,
211
- mangleNames: shouldMangleNames,
212
- onReady: () => log("Watching"),
213
- forceQuineCheats: shouldforceQuineCheats
214
- })
215
- autoExit = !1
216
224
  }
217
225
  break
218
226
  case "pull":
@@ -222,8 +230,9 @@ switch (commands[0]) {
222
230
  if (!script) {
223
231
  logError("Must provide the script to pull\n")
224
232
  logHelp()
225
- break
233
+ process.exit(1)
226
234
  }
235
+ complainAboutUnrecognisedOptions()
227
236
  const sourcePath = commands[2] || "."
228
237
  await pull(sourcePath, hackmudPath, script).catch(error => {
229
238
  console.error(error)
@@ -233,8 +242,9 @@ switch (commands[0]) {
233
242
  break
234
243
  case "sync-macros":
235
244
  {
236
- const hackmudPath = getHackmudPath(),
237
- { macrosSynced, usersSynced } = await syncMacros(hackmudPath)
245
+ const hackmudPath = getHackmudPath()
246
+ complainAboutUnrecognisedOptions()
247
+ const { macrosSynced, usersSynced } = await syncMacros(hackmudPath)
238
248
  log(`Synced ${macrosSynced} macros to ${usersSynced} users`)
239
249
  }
240
250
  break
@@ -243,18 +253,20 @@ switch (commands[0]) {
243
253
  case "gen-dts":
244
254
  case "gen-types":
245
255
  {
246
- const target = commands[1]
256
+ const hackmudPath = getHackmudPath(),
257
+ target = commands[1]
247
258
  if (!target) {
248
259
  logError("Must provide target directory\n")
249
260
  logHelp()
250
- break
261
+ process.exit(1)
251
262
  }
263
+ complainAboutUnrecognisedOptions()
252
264
  const sourcePath = resolve(target),
253
265
  outputPath = commands[2] || "./player.d.ts",
254
- typeDeclaration = await generateTypeDeclaration(sourcePath, getHackmudPath())
266
+ typeDeclaration = await generateTypeDeclaration(sourcePath, hackmudPath)
255
267
  let typeDeclarationPath = resolve(outputPath)
256
268
  await writeFile(typeDeclarationPath, typeDeclaration).catch(error => {
257
- assert(error instanceof Error, "src/bin/hsm.ts:340:35")
269
+ assert(error instanceof Error, "src/bin/hsm.ts:343:35")
258
270
  if ("EISDIR" != error.code) throw error
259
271
  typeDeclarationPath = resolve(typeDeclarationPath, "player.d.ts")
260
272
  return writeFile(typeDeclarationPath, typeDeclaration)
@@ -266,99 +278,6 @@ switch (commands[0]) {
266
278
  case "h":
267
279
  logHelp()
268
280
  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
281
  default:
363
282
  commands[0] && logError(`Unknown command: ${colourL(commands[0])}\n`)
364
283
  logHelp()
@@ -432,7 +351,7 @@ function logError(message) {
432
351
  process.exitCode = 1
433
352
  }
434
353
  function getHackmudPath() {
435
- const hackmudPathOption = options.get("hackmud-path")
354
+ const hackmudPathOption = popOption("hackmud-path")
436
355
  if (null != hackmudPathOption && "string" != typeof hackmudPathOption) {
437
356
  logError(`Option ${colourN("--hackmud-path")} must be a string, got ${colourV(hackmudPathOption)}\n`)
438
357
  logHelp()
@@ -444,3 +363,32 @@ function getHackmudPath() {
444
363
  ("win32" == process.platform ? resolve(process.env.APPDATA, "hackmud") : resolve(homedir(), ".config/hackmud"))
445
364
  )
446
365
  }
366
+ function assertOptionIsBoolean(option) {
367
+ if ("boolean" != typeof option.value) {
368
+ logError(`The value for ${formatOption(option.name)} must be ${colourV("true")} or ${colourV("false")}\n`)
369
+ logHelp()
370
+ process.exit(1)
371
+ }
372
+ }
373
+ function popOption(...names) {
374
+ const presentOptionNames = names.filter(name => options.has(name))
375
+ if (!presentOptionNames.length) return
376
+ const presentOptionNamesWithDashDash = presentOptionNames.map(formatOption)
377
+ if (presentOptionNames.length > 1) {
378
+ logError(
379
+ `The options ${presentOptionNamesWithDashDash.join(", ")} are aliases for each other. Please only specify one`
380
+ )
381
+ process.exit(1)
382
+ }
383
+ const value = options.get(presentOptionNames[0])
384
+ options.delete(presentOptionNames[0])
385
+ return { name: presentOptionNamesWithDashDash[0], value }
386
+ }
387
+ function complainAboutUnrecognisedOptions() {
388
+ if (options.size) {
389
+ logError(
390
+ `Unrecognised option${options.size > 1 ? "s" : ""}: ${[...options.keys()].map(formatOption).join(", ")}`
391
+ )
392
+ process.exit(1)
393
+ }
394
+ }
package/env.d.ts CHANGED
@@ -151,7 +151,16 @@ type Fullsec = Subscripts & PlayerFullsec & {
151
151
  market: {
152
152
  /** **FULLSEC** */ browse: {
153
153
  (args:
154
- Partial<{ seller: string, listed_before: number, listed_after: number, cost: number | string } & Omit<CliUpgrade, "rarity">>
154
+ Partial<{
155
+ seller: string | MongoQuerySelector<string>,
156
+ listed_before: number | MongoQuerySelector<number>,
157
+ listed_after: number,
158
+ cost: number | MongoQuerySelector<number> | string,
159
+ rarity: UpgradeRarityNumber | MongoQuerySelector<UpgradeRarityNumber>,
160
+ name: string | MongoQuerySelector<string>
161
+ } & Omit<{
162
+ [k in keyof CliUpgrade]: CliUpgrade[k] | MongoQuerySelector<CliUpgrade[k]>
163
+ }, "rarity">>
155
164
  ): { i: string, name: string, rarity: Upgrade["rarity"], cost: number }[] | ScriptFailure
156
165
 
157
166
  <I extends string>(args: { i: I }): {
@@ -710,10 +719,50 @@ type Nullsec = Lowsec & PlayerNullsec & {
710
719
  }
711
720
  }
712
721
 
722
+ type MongoTypeString = "minKey" | "double" | "string" | "object" | "array" | "binData" | "undefined" | "objectId" |
723
+ "bool" | "date" | "null" | "regex" | "dbPointer" | "javascript" | "symbol" | "int" | "timestamp" | "long" | "decimal" | "maxKey";
724
+ type MongoTypeNumber = -1 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 127;
725
+
713
726
  type MongoValue = string | number | boolean | Date | MongoValue[] | { [key: string]: MongoValue } | null
714
727
 
715
728
  type MongoCommandValue = string | number | boolean | Date | MongoCommandValue[] | { [key: string]: MongoCommandValue } |
716
- null | undefined
729
+ null | undefined
730
+
731
+ /**
732
+ * Currently unused
733
+ */
734
+ type MongoLogicalSelectors<T extends MongoValue = MongoValue> = {
735
+ $not: T | MongoComparisonSelectors<T> | MongoLogicalSelectors<T>
736
+ $nor: T[]
737
+ $or: T[]
738
+ $and: T[]
739
+ }
740
+
741
+ type MongoArraySelectors<T extends Array<MongoValue> = Array<MongoValue>> = {
742
+ $all: T
743
+ $elemMatch: T
744
+ $size: number
745
+ }
746
+
747
+ type MongoComparisonSelectors<T extends MongoValue = MongoValue> = {
748
+ $eq: T
749
+ $gt: T
750
+ $gte: T
751
+ $in: T[]
752
+ $lt: T
753
+ $lte: T
754
+ $ne: T
755
+ $nin: T[]
756
+ }
757
+
758
+ type MongoElementSelectors = {
759
+ $exists: boolean
760
+ $type: MongoTypeNumber | MongoTypeString
761
+ }
762
+
763
+ type MongoQuerySelector<T extends MongoValue = MongoValue> = Partial<T extends MongoValue[] ?
764
+ (MongoArraySelectors<T> & MongoElementSelectors & MongoComparisonSelectors<T>) :
765
+ (MongoElementSelectors & MongoComparisonSelectors<T>)>
717
766
 
718
767
  type Query = { [key: string]: MongoValue | Query } & { _id?: Id, $in?: MongoValue[] }
719
768
  type Projection = Record<string, boolean | 0 | 1>
@@ -1,5 +1,6 @@
1
1
  import { readDirectoryWithStats } from "@samual/lib/readDirectoryWithStats"
2
2
  import { basename, resolve } from "path"
3
+ import * as PathPosix from "path/posix"
3
4
  async function generateTypeDeclaration(sourceDirectory, hackmudPath) {
4
5
  const users = new Set()
5
6
  if (hackmudPath)
@@ -29,7 +30,7 @@ async function generateTypeDeclaration(sourceDirectory, hackmudPath) {
29
30
  }
30
31
  })
31
32
  )
32
- sourceDirectory = resolve(sourceDirectory)
33
+ sourceDirectory = PathPosix.resolve(sourceDirectory)
33
34
  let o = ""
34
35
  for (const script of wildScripts) o += `type $${script}$ = typeof import("${sourceDirectory}/${script}").default\n`
35
36
  o += "\n"
package/index.js CHANGED
@@ -7,6 +7,7 @@ export { syncMacros } from "./syncMacros.js"
7
7
  export { watch } from "./watch.js"
8
8
  import "@samual/lib/readDirectoryWithStats"
9
9
  import "path"
10
+ import "path/posix"
10
11
  import "@babel/generator"
11
12
  import "@babel/parser"
12
13
  import "@babel/plugin-proposal-decorators"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hackmud-script-manager",
3
- "version": "0.20.4-d2fc4af",
3
+ "version": "0.20.4-d4996d7",
4
4
  "description": "Script manager for game hackmud, with minification, TypeScript support, and player script type definition generation.",
5
5
  "keywords": [
6
6
  "api",
@@ -323,7 +323,7 @@ async function processScript(
323
323
  trailingComma: "none"
324
324
  })
325
325
  }
326
- code = postprocess(code, seclevel, uniqueId)
326
+ code = postprocess(code, uniqueId)
327
327
  if (includesIllegalString(code))
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'
@@ -43,15 +43,6 @@ async function minify(file, { uniqueId = "00000000000", mangleNames = !1, forceQ
43
43
  )
44
44
  }
45
45
  }
46
- const hashGReferencePaths = getReferencePathsToGlobal(`$${uniqueId}$GLOBAL$`, program)
47
- if (hashGReferencePaths.length > 3) {
48
- for (const path of hashGReferencePaths) path.replaceWith(t.identifier(`_${uniqueId}_G_`))
49
- mainFunctionPath.node.body.body.unshift(
50
- t.variableDeclaration("let", [
51
- t.variableDeclarator(t.identifier(`_${uniqueId}_G_`), t.identifier(`$${uniqueId}$GLOBAL$`))
52
- ])
53
- )
54
- }
55
46
  const jsonValues = []
56
47
  let scriptBeforeJSONValueReplacement,
57
48
  comment,
@@ -61,7 +52,7 @@ async function minify(file, { uniqueId = "00000000000", mangleNames = !1, forceQ
61
52
  traverse(fileBeforeJSONValueReplacement, {
62
53
  MemberExpression({ node: memberExpression }) {
63
54
  if (!memberExpression.computed) {
64
- assert("Identifier" == memberExpression.property.type, "src/processScript/minify.ts:127:60")
55
+ assert("Identifier" == memberExpression.property.type, "src/processScript/minify.ts:115:60")
65
56
  if ("prototype" == memberExpression.property.name) {
66
57
  memberExpression.computed = !0
67
58
  memberExpression.property = t.identifier(`_${uniqueId}_PROTOTYPE_PROPERTY_`)
@@ -173,7 +164,7 @@ async function minify(file, { uniqueId = "00000000000", mangleNames = !1, forceQ
173
164
  },
174
165
  MemberExpression({ node: memberExpression }) {
175
166
  if (!memberExpression.computed) {
176
- assert("Identifier" == memberExpression.property.type, "src/processScript/minify.ts:261:62")
167
+ assert("Identifier" == memberExpression.property.type, "src/processScript/minify.ts:249:62")
177
168
  if (!(memberExpression.property.name.length < 3)) {
178
169
  memberExpression.computed = !0
179
170
  memberExpression.property = t.stringLiteral(memberExpression.property.name)
@@ -247,7 +238,7 @@ async function minify(file, { uniqueId = "00000000000", mangleNames = !1, forceQ
247
238
  })
248
239
  await Promise.all(promises)
249
240
  const functionDeclaration = file.program.body[0]
250
- assert("FunctionDeclaration" == functionDeclaration.type, "src/processScript/minify.ts:366:61")
241
+ assert("FunctionDeclaration" == functionDeclaration.type, "src/processScript/minify.ts:354:61")
251
242
  if (jsonValues.length) {
252
243
  hasComment = !0
253
244
  if (1 == jsonValues.length)
@@ -259,7 +250,10 @@ async function minify(file, { uniqueId = "00000000000", mangleNames = !1, forceQ
259
250
  t.memberExpression(
260
251
  t.taggedTemplateExpression(
261
252
  t.memberExpression(
262
- t.callExpression(t.identifier(`$${uniqueId}$SUBSCRIPT$scripts$quine$`), []),
253
+ t.callExpression(
254
+ t.identifier(`$${uniqueId}$4$SUBSCRIPT$scripts$quine$`),
255
+ []
256
+ ),
263
257
  t.identifier("split")
264
258
  ),
265
259
  t.templateLiteral([t.templateElement({ raw: "\t", cooked: "\t" }, !0)], [])
@@ -283,7 +277,7 @@ async function minify(file, { uniqueId = "00000000000", mangleNames = !1, forceQ
283
277
  t.memberExpression(
284
278
  t.taggedTemplateExpression(
285
279
  t.memberExpression(
286
- t.callExpression(t.identifier(`$${uniqueId}$SUBSCRIPT$scripts$quine$`), []),
280
+ t.callExpression(t.identifier(`$${uniqueId}$4$SUBSCRIPT$scripts$quine$`), []),
287
281
  t.identifier("split")
288
282
  ),
289
283
  t.templateLiteral([t.templateElement({ raw: "\t", cooked: "\t" }, !0)], [])
@@ -308,7 +302,7 @@ async function minify(file, { uniqueId = "00000000000", mangleNames = !1, forceQ
308
302
  t.memberExpression(
309
303
  t.taggedTemplateExpression(
310
304
  t.memberExpression(
311
- t.callExpression(t.identifier(`$${uniqueId}$SUBSCRIPT$scripts$quine$`), []),
305
+ t.callExpression(t.identifier(`$${uniqueId}$4$SUBSCRIPT$scripts$quine$`), []),
312
306
  t.identifier("split")
313
307
  ),
314
308
  t.templateLiteral([t.templateElement({ raw: "\t", cooked: "\t" }, !0)], [])
@@ -364,7 +358,7 @@ async function minify(file, { uniqueId = "00000000000", mangleNames = !1, forceQ
364
358
  )
365
359
  }
366
360
  if (1 == forceQuineCheats) return code
367
- assert(scriptBeforeJSONValueReplacement, "src/processScript/minify.ts:497:43")
361
+ assert(scriptBeforeJSONValueReplacement, "src/processScript/minify.ts:485:43")
368
362
  return (
369
363
  countHackmudCharacters(scriptBeforeJSONValueReplacement) <=
370
364
  countHackmudCharacters(code) + Number(hasComment)
@@ -380,7 +374,7 @@ function parseObjectExpression(node, o) {
380
374
  "Identifier" == property.key.type ||
381
375
  "NumericLiteral" == property.key.type ||
382
376
  "StringLiteral" == property.key.type,
383
- "src/processScript/minify.ts:519:4"
377
+ "src/processScript/minify.ts:507:4"
384
378
  )
385
379
  if ("ArrayExpression" == property.value.type) {
386
380
  const childArray = []
@@ -1 +1 @@
1
- export declare const postprocess: (code: string, seclevel: number, uniqueId: string) => string;
1
+ export declare const postprocess: (code: string, uniqueId: string) => string;
@@ -1,12 +1,12 @@
1
- const postprocess = (code, seclevel, uniqueId) =>
1
+ const postprocess = (code, uniqueId) =>
2
2
  code
3
- .replace(/^function\s*\w+\(/, "function(")
3
+ .replace(/^function\s*[\w$]+\(/, "function(")
4
4
  .replace(RegExp(`\\$${uniqueId}\\$\\\\(?:\\\\)?\\$SC_DOLLAR\\$`, "g"), "S\\C$")
5
5
  .replace(RegExp(`\\$${uniqueId}\\$\\\\(?:\\\\)?\\$DB_DOLLAR\\$`, "g"), "D\\B$")
6
6
  .replace(RegExp(`\\$${uniqueId}\\$\\\\(?:\\\\)?\\$D\\$`, "g"), "_\\_D_S")
7
7
  .replace(RegExp(`\\$${uniqueId}\\$\\\\(?:\\\\)?\\$FMCL\\$`, "g"), "_\\_FMCL_")
8
8
  .replace(RegExp(`\\$${uniqueId}\\$\\\\(?:\\\\)?\\$G\\$`, "g"), "_\\_G_")
9
- .replace(RegExp(`\\$${uniqueId}\\$SUBSCRIPT\\$(\\w+)\\$(\\w+)\\$`, "g"), `#${"nlmhf"[seclevel]}s.$1.$2`)
9
+ .replace(RegExp(`\\$${uniqueId}\\$(\\d)\\$SUBSCRIPT\\$(\\w+)\\$(\\w+)\\$`, "g"), "#$1s.$2.$3")
10
10
  .replace(RegExp(`\\$${uniqueId}\\$DEBUG\\$`, "g"), "#D")
11
11
  .replace(RegExp(`\\$${uniqueId}\\$FMCL\\$`, "g"), "#FMCL")
12
12
  .replace(RegExp(`\\$${uniqueId}\\$GLOBAL\\$`, "g"), "#G")
@@ -98,8 +98,10 @@ async function preprocess(code, { uniqueId = "00000000000" } = {}) {
98
98
  t.stringLiteral(resolve("proxy-polyfill/src/proxy.js", import.meta.url).slice(7))
99
99
  )
100
100
  )
101
- return 1 == program.node.body.length && "FunctionDeclaration" == program.node.body[0].type ?
102
- { code: "export default " + generate(file).code }
103
- : { code: generate(file).code }
101
+ if (1 == program.node.body.length && "FunctionDeclaration" == program.node.body[0].type)
102
+ throw Error(
103
+ "Scripts that only contain a single function declaration are no longer supported.\nPrefix the function declaration with `export default`."
104
+ )
105
+ return { code: generate(file).code }
104
106
  }
105
107
  export { preprocess }
@@ -22,9 +22,7 @@ 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_`,
26
- exports = new Map(),
27
- liveExports = new Map()
25
+ const topFunctionName = `_${uniqueId}_SCRIPT_`
28
26
  let program
29
27
  traverse(file, {
30
28
  Program(path) {
@@ -70,30 +68,30 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
70
68
  const referencePath = FunctionReferencePaths[0]
71
69
  assert(
72
70
  "MemberExpression" == referencePath.parent.type,
73
- "src/processScript/transform.ts:105:8 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
71
+ "src/processScript/transform.ts:103:8 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
74
72
  )
75
73
  assert(
76
74
  "Identifier" == referencePath.parent.property.type,
77
- "src/processScript/transform.ts:110:8 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
75
+ "src/processScript/transform.ts:108:8 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
78
76
  )
79
77
  assert(
80
78
  "prototype" == referencePath.parent.property.name,
81
- "src/processScript/transform.ts:115:8 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
79
+ "src/processScript/transform.ts:113:8 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
82
80
  )
83
81
  referencePath.parentPath.replaceWith(createGetFunctionPrototypeNode())
84
82
  } else {
85
83
  for (const referencePath of FunctionReferencePaths) {
86
84
  assert(
87
85
  "MemberExpression" == referencePath.parent.type,
88
- "src/processScript/transform.ts:123:9 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
86
+ "src/processScript/transform.ts:121:9 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
89
87
  )
90
88
  assert(
91
89
  "Identifier" == referencePath.parent.property.type,
92
- "src/processScript/transform.ts:128:9 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
90
+ "src/processScript/transform.ts:126:9 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
93
91
  )
94
92
  assert(
95
93
  "prototype" == referencePath.parent.property.name,
96
- "src/processScript/transform.ts:133:9 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
94
+ "src/processScript/transform.ts:131:9 `Function` isn't available in hackmud, only `Function.prototype` is accessible"
97
95
  )
98
96
  functionDotPrototypeIsReferencedMultipleTimes = !0
99
97
  referencePath.parentPath.replaceWith(t.identifier(`_${uniqueId}_FUNCTION_DOT_PROTOTYPE_`))
@@ -101,40 +99,40 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
101
99
  functionDotPrototypeIsReferencedMultipleTimes = !0
102
100
  }
103
101
  }
104
- const neededSubscriptLets = new Set()
102
+ const neededSubscriptLets = new Map()
105
103
  let detectedSeclevel = 4
106
104
  for (const fakeSubscriptObjectName of ["$fs", "$4s", "$s"])
107
- program.scope.hasGlobal(fakeSubscriptObjectName) && processFakeSubscriptObject(fakeSubscriptObjectName)
105
+ program.scope.hasGlobal(fakeSubscriptObjectName) && processFakeSubscriptObject(fakeSubscriptObjectName, 4)
108
106
  for (const fakeSubscriptObjectName of ["$hs", "$3s"])
109
107
  if (program.scope.hasGlobal(fakeSubscriptObjectName)) {
110
108
  detectedSeclevel = 3
111
- processFakeSubscriptObject(fakeSubscriptObjectName)
109
+ processFakeSubscriptObject(fakeSubscriptObjectName, 3)
112
110
  }
113
111
  for (const fakeSubscriptObjectName of ["$ms", "$2s"])
114
112
  if (program.scope.hasGlobal(fakeSubscriptObjectName)) {
115
113
  detectedSeclevel = 2
116
- processFakeSubscriptObject(fakeSubscriptObjectName)
114
+ processFakeSubscriptObject(fakeSubscriptObjectName, 2)
117
115
  }
118
116
  for (const fakeSubscriptObjectName of ["$ls", "$1s"])
119
117
  if (program.scope.hasGlobal(fakeSubscriptObjectName)) {
120
118
  detectedSeclevel = 1
121
- processFakeSubscriptObject(fakeSubscriptObjectName)
119
+ processFakeSubscriptObject(fakeSubscriptObjectName, 1)
122
120
  }
123
121
  for (const fakeSubscriptObjectName of ["$ns", "$0s"])
124
122
  if (program.scope.hasGlobal(fakeSubscriptObjectName)) {
125
123
  detectedSeclevel = 0
126
- processFakeSubscriptObject(fakeSubscriptObjectName)
124
+ processFakeSubscriptObject(fakeSubscriptObjectName, 0)
127
125
  }
128
126
  seclevel = Math.min(seclevel, detectedSeclevel)
129
127
  const neededDbMethodLets = new Set()
130
128
  if (program.scope.hasGlobal("$db"))
131
129
  for (const referencePath of getReferencePathsToGlobal("$db", program)) {
132
- assert("MemberExpression" == referencePath.parentPath.node.type, "src/processScript/transform.ts:187:69")
133
- assert("Identifier" == referencePath.parentPath.node.property.type, "src/processScript/transform.ts:188:72")
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")
134
132
  const databaseOpMethodName = referencePath.parentPath.node.property.name
135
133
  assert(
136
134
  validDBMethods.includes(databaseOpMethodName),
137
- `src/processScript/transform.ts:194:8 invalid db method "${databaseOpMethodName}", valid db methods are "${validDBMethods.join('", "')}"`
135
+ `src/processScript/transform.ts:192:8 invalid db method "${databaseOpMethodName}", valid db methods are "${validDBMethods.join('", "')}"`
138
136
  )
139
137
  if ("CallExpression" == referencePath.parentPath.parentPath?.type)
140
138
  referencePath.parentPath.replaceWith(t.identifier(`$${uniqueId}$DB$${databaseOpMethodName}$`))
@@ -157,9 +155,10 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
157
155
  if (program.scope.hasGlobal("$FMCL"))
158
156
  for (const referencePath of getReferencePathsToGlobal("$FMCL", program))
159
157
  referencePath.replaceWith(t.identifier(`$${uniqueId}$FMCL$`))
160
- if (program.scope.hasGlobal("$G"))
158
+ let needG = program.scope.hasGlobal("$G")
159
+ if (needG)
161
160
  for (const referencePath of getReferencePathsToGlobal("$G", program))
162
- referencePath.replaceWith(t.identifier(`$${uniqueId}$GLOBAL$`))
161
+ referencePath.replaceWith(t.identifier(`_${uniqueId}_G_`))
163
162
  if (program.scope.hasGlobal("_SECLEVEL"))
164
163
  for (const referencePath of getReferencePathsToGlobal("_SECLEVEL", program))
165
164
  referencePath.replaceWith(t.numericLiteral(seclevel))
@@ -193,11 +192,12 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
193
192
  "ExportSpecifier" == specifier.type,
194
193
  `src/processScript/transform.ts:276:51 ${specifier.type} is currently unsupported`
195
194
  )
196
- const exportedName =
197
- "Identifier" == specifier.exported.type ? specifier.exported.name : specifier.exported.value
198
- "default" == exportedName ?
199
- (exportDefaultName = specifier.local.name)
200
- : exports.set(specifier.local.name, exportedName)
195
+ if (
196
+ "default" !=
197
+ ("Identifier" == specifier.exported.type ? specifier.exported.name : specifier.exported.value)
198
+ )
199
+ throw Error("Only default exports are supported")
200
+ exportDefaultName = specifier.local.name
201
201
  }
202
202
  }
203
203
  const globalBlock = t.blockStatement([])
@@ -223,10 +223,6 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
223
223
  t.returnStatement(t.callExpression(t.identifier(exportDefaultName), []))
224
224
  ])
225
225
  ))
226
- if ("const" != statement.kind && exports.has(identifierName)) {
227
- liveExports.set(identifierName, exports.get(identifierName))
228
- exports.delete(identifierName)
229
- }
230
226
  globalBlock.body.push(
231
227
  t.variableDeclaration("let", [t.variableDeclarator(t.identifier(identifierName))])
232
228
  )
@@ -291,34 +287,16 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
291
287
  }
292
288
  program.node.body = [mainFunction]
293
289
  if (globalBlock.body.length) {
294
- ;(exports.size || liveExports.size) &&
295
- mainFunction.body.body.push(
296
- t.returnStatement(
297
- t.objectExpression([
298
- ...[...exports].map(([local, exported]) =>
299
- t.objectProperty(t.identifier(exported), t.identifier(local))
300
- ),
301
- ...[...liveExports].map(([local, exported]) =>
302
- t.objectMethod(
303
- "get",
304
- t.identifier(exported),
305
- [],
306
- t.blockStatement([t.returnStatement(t.identifier(local))])
307
- )
308
- )
309
- ])
310
- )
311
- )
312
290
  program.scope.crawl()
313
291
  const globalBlockVariables = new Set()
314
292
  let hoistedGlobalBlockFunctions = 0
315
293
  for (const [globalBlockIndex, globalBlockStatement] of [...globalBlock.body.entries()].reverse())
316
294
  if ("VariableDeclaration" == globalBlockStatement.type) {
317
- assert(1 == globalBlockStatement.declarations.length, "src/processScript/transform.ts:410:59")
295
+ assert(1 == globalBlockStatement.declarations.length, "src/processScript/transform.ts:390:59")
318
296
  const declarator = globalBlockStatement.declarations[0]
319
297
  assert(
320
298
  "Identifier" == declarator.id.type,
321
- `src/processScript/transform.ts:414:51 declarator.id.type was "${declarator.id.type}"`
299
+ `src/processScript/transform.ts:394:51 declarator.id.type was "${declarator.id.type}"`
322
300
  )
323
301
  program.scope.crawl()
324
302
  if (program.scope.hasGlobal(declarator.id.name)) {
@@ -333,15 +311,16 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
333
311
  Object.keys(program.scope.globals).some(global => globalBlockVariables.has(global))
334
312
  ) {
335
313
  const binding = program.scope.getBinding(declarator.id.name)
336
- assert(binding, "src/processScript/transform.ts:433:23")
314
+ assert(binding, "src/processScript/transform.ts:413:23")
337
315
  for (const referencePath of binding.referencePaths) {
338
- assert("Identifier" == referencePath.node.type, "src/processScript/transform.ts:436:56")
316
+ assert("Identifier" == referencePath.node.type, "src/processScript/transform.ts:416:56")
339
317
  referencePath.replaceWith(
340
318
  t.memberExpression(
341
- t.identifier(`$${uniqueId}$GLOBAL$`),
319
+ t.identifier(`_${uniqueId}_G_`),
342
320
  t.identifier(referencePath.node.name)
343
321
  )
344
322
  )
323
+ needG = !0
345
324
  }
346
325
  for (const referencePath of binding.constantViolations)
347
326
  if ("AssignmentExpression" == referencePath.node.type)
@@ -350,12 +329,13 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
350
329
  clearObject(node)
351
330
  Object.assign(
352
331
  node,
353
- t.memberExpression(t.identifier(`$${uniqueId}$GLOBAL$`), t.identifier(name))
332
+ t.memberExpression(t.identifier(`_${uniqueId}_G_`), t.identifier(name))
354
333
  )
334
+ needG = !0
355
335
  }
356
336
  globalBlockPath.remove()
357
337
  globalBlockStatementPath.remove()
358
- declarator.init &&
338
+ if (declarator.init) {
359
339
  globalBlock.body.splice(
360
340
  globalBlockIndex,
361
341
  0,
@@ -363,13 +343,15 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
363
343
  t.assignmentExpression(
364
344
  "=",
365
345
  t.memberExpression(
366
- t.identifier(`$${uniqueId}$GLOBAL$`),
346
+ t.identifier(`_${uniqueId}_G_`),
367
347
  t.identifier(declarator.id.name)
368
348
  ),
369
349
  declarator.init
370
350
  )
371
351
  )
372
352
  )
353
+ needG = !0
354
+ }
373
355
  } else {
374
356
  globalBlockPath.remove()
375
357
  globalBlockStatementPath.remove()
@@ -379,22 +361,20 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
379
361
  } else globalBlockVariables.add(declarator.id.name)
380
362
  } else if ("ClassDeclaration" == globalBlockStatement.type) {
381
363
  program.scope.crawl()
382
- assert(globalBlockStatement.id, "src/processScript/transform.ts:487:37")
364
+ assert(globalBlockStatement.id, "src/processScript/transform.ts:473:37")
383
365
  if (program.scope.hasGlobal(globalBlockStatement.id.name)) {
384
366
  globalBlock.body.splice(globalBlockIndex, 1)
385
367
  const [globalBlockPath] = program.unshiftContainer("body", globalBlock),
386
368
  [globalBlockStatementPath] = program.unshiftContainer("body", globalBlockStatement)
387
369
  program.scope.crawl()
388
370
  const binding = program.scope.getBinding(globalBlockStatement.id.name)
389
- assert(binding, "src/processScript/transform.ts:499:22")
371
+ assert(binding, "src/processScript/transform.ts:485:22")
390
372
  for (const referencePath of binding.referencePaths) {
391
- assert("Identifier" == referencePath.node.type, "src/processScript/transform.ts:502:55")
373
+ assert("Identifier" == referencePath.node.type, "src/processScript/transform.ts:488:55")
392
374
  referencePath.replaceWith(
393
- t.memberExpression(
394
- t.identifier(`$${uniqueId}$GLOBAL$`),
395
- t.identifier(referencePath.node.name)
396
- )
375
+ t.memberExpression(t.identifier(`_${uniqueId}_G_`), t.identifier(referencePath.node.name))
397
376
  )
377
+ needG = !0
398
378
  }
399
379
  globalBlockPath.remove()
400
380
  globalBlockStatementPath.remove()
@@ -405,7 +385,7 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
405
385
  t.assignmentExpression(
406
386
  "=",
407
387
  t.memberExpression(
408
- t.identifier(`$${uniqueId}$GLOBAL$`),
388
+ t.identifier(`_${uniqueId}_G_`),
409
389
  t.identifier(globalBlockStatement.id.name)
410
390
  ),
411
391
  t.classExpression(
@@ -417,13 +397,9 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
417
397
  )
418
398
  )
419
399
  )
400
+ needG = !0
420
401
  }
421
402
  }
422
- if (program.scope.hasGlobal("_EXPORTS"))
423
- for (const referencePath of getReferencePathsToGlobal("_EXPORTS", program))
424
- referencePath.replaceWith(
425
- t.arrayExpression([...exports.keys(), ...liveExports.keys()].map(name => t.stringLiteral(name)))
426
- )
427
403
  globalBlock.body.length &&
428
404
  mainFunction.body.body.splice(
429
405
  hoistedGlobalBlockFunctions,
@@ -522,12 +498,12 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
522
498
  mainFunction.body.body.unshift(
523
499
  t.variableDeclaration(
524
500
  "let",
525
- [...neededSubscriptLets].map(name =>
501
+ [...neededSubscriptLets].map(([name, seclevel]) =>
526
502
  t.variableDeclarator(
527
503
  t.identifier(`_${uniqueId}_SUBSCRIPT_${name}_`),
528
504
  t.arrowFunctionExpression(
529
505
  [t.restElement(t.identifier("args"))],
530
- t.callExpression(t.identifier(`$${uniqueId}$SUBSCRIPT$${name}$`), [
506
+ t.callExpression(t.identifier(`$${uniqueId}$${seclevel}$SUBSCRIPT$${name}$`), [
531
507
  t.spreadElement(t.identifier("args"))
532
508
  ])
533
509
  )
@@ -535,6 +511,12 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
535
511
  )
536
512
  )
537
513
  )
514
+ needG &&
515
+ mainFunction.body.body.unshift(
516
+ t.variableDeclaration("let", [
517
+ t.variableDeclarator(t.identifier(`_${uniqueId}_G_`), t.identifier(`$${uniqueId}$GLOBAL$`))
518
+ ])
519
+ )
538
520
  traverse(file, {
539
521
  BlockStatement({ node: blockStatement }) {
540
522
  for (const [index, functionDeclaration] of blockStatement.body.entries())
@@ -555,7 +537,7 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
555
537
  }
556
538
  },
557
539
  ClassBody({ node: classBody, scope, parent }) {
558
- assert(t.isClass(parent), "src/processScript/transform.ts:669:30")
540
+ assert(t.isClass(parent), "src/processScript/transform.ts:658:30")
559
541
  let thisIsReferenced = !1
560
542
  for (const classMethod of classBody.body) {
561
543
  if ("ClassMethod" != classMethod.type) continue
@@ -661,36 +643,37 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
661
643
  t.identifier("prototype")
662
644
  )
663
645
  }
664
- function processFakeSubscriptObject(fakeSubscriptObjectName) {
646
+ function processFakeSubscriptObject(fakeSubscriptObjectName, seclevel) {
665
647
  for (const referencePath of getReferencePathsToGlobal(fakeSubscriptObjectName, program)) {
666
- assert("MemberExpression" == referencePath.parent.type, "src/processScript/transform.ts:785:60")
648
+ assert("MemberExpression" == referencePath.parent.type, "src/processScript/transform.ts:774:60")
667
649
  assert("Identifier" == referencePath.parent.property.type)
668
650
  assert(
669
651
  "MemberExpression" == referencePath.parentPath.parentPath?.node.type,
670
- "src/processScript/transform.ts:787:81"
652
+ "src/processScript/transform.ts:776:81"
671
653
  )
672
654
  assert(
673
655
  "Identifier" == referencePath.parentPath.parentPath.node.property.type,
674
- "src/processScript/transform.ts:788:83"
656
+ "src/processScript/transform.ts:777:83"
675
657
  )
676
658
  assert(
677
659
  /^[_a-z][\d_a-z]{0,24}$/.test(referencePath.parent.property.name),
678
- `src/processScript/transform.ts:792:8 invalid user "${referencePath.parent.property.name}" in subscript`
660
+ `src/processScript/transform.ts:781:8 invalid user "${referencePath.parent.property.name}" in subscript`
679
661
  )
680
662
  assert(
681
663
  /^[_a-z][\d_a-z]{0,24}$/.test(referencePath.parentPath.parentPath.node.property.name),
682
- `src/processScript/transform.ts:797:8 invalid script name "${referencePath.parentPath.parentPath.node.property.name}" in subscript`
664
+ `src/processScript/transform.ts:786:8 invalid script name "${referencePath.parentPath.parentPath.node.property.name}" in subscript`
683
665
  )
684
666
  if ("CallExpression" == referencePath.parentPath.parentPath.parentPath?.type)
685
667
  referencePath.parentPath.parentPath.replaceWith(
686
668
  t.identifier(
687
- `$${uniqueId}$SUBSCRIPT$${referencePath.parent.property.name}$${referencePath.parentPath.parentPath.node.property.name}$`
669
+ `$${uniqueId}$${seclevel}$SUBSCRIPT$${referencePath.parent.property.name}$${referencePath.parentPath.parentPath.node.property.name}$`
688
670
  )
689
671
  )
690
672
  else {
691
673
  const name = `${referencePath.parent.property.name}$${referencePath.parentPath.parentPath.node.property.name}`
692
674
  referencePath.parentPath.parentPath.replaceWith(t.identifier(`_${uniqueId}_SUBSCRIPT_${name}_`))
693
- neededSubscriptLets.add(name)
675
+ const maxSecLevel = Math.max(neededSubscriptLets.get(name) || 0, seclevel)
676
+ neededSubscriptLets.set(name, maxSecLevel)
694
677
  }
695
678
  }
696
679
  }
package/watch.js CHANGED
@@ -4,11 +4,12 @@ import { countHackmudCharacters } from "@samual/lib/countHackmudCharacters"
4
4
  import { readDirectoryWithStats } from "@samual/lib/readDirectoryWithStats"
5
5
  import { writeFilePersistent } from "@samual/lib/writeFilePersistent"
6
6
  import { watch as watch$1 } from "chokidar"
7
- import { readFile, writeFile } from "fs/promises"
7
+ import { stat, readFile, writeFile } from "fs/promises"
8
8
  import { extname, basename, resolve } from "path"
9
9
  import { supportedExtensions } from "./constants.js"
10
10
  import { generateTypeDeclaration } from "./generateTypeDeclaration.js"
11
11
  import { processScript } from "./processScript/index.js"
12
+ import "path/posix"
12
13
  import "@babel/generator"
13
14
  import "@babel/parser"
14
15
  import "@babel/plugin-proposal-decorators"
@@ -58,6 +59,7 @@ async function watch(
58
59
  } = {}
59
60
  ) {
60
61
  if (!scripts.length) throw Error("scripts option was an empty array")
62
+ if (!(await stat(sourceDirectory)).isDirectory()) throw Error("Target folder must be a folder")
61
63
  const scriptNamesToUsers = new Cache(_scriptName => new Set()),
62
64
  wildScriptUsers = new Set(),
63
65
  wildUserScripts = new Set()
@@ -135,7 +137,7 @@ async function watch(
135
137
  forceQuineCheats
136
138
  }))
137
139
  } catch (error) {
138
- assert(error instanceof Error, "src/watch.ts:141:36")
140
+ assert(error instanceof Error, "src/watch.ts:146:36")
139
141
  onPush?.({ path, users: [], characterCount: 0, error })
140
142
  return
141
143
  }
@@ -180,7 +182,7 @@ async function watch(
180
182
  forceQuineCheats
181
183
  }))
182
184
  } catch (error) {
183
- assert(error instanceof Error, "src/watch.ts:177:35")
185
+ assert(error instanceof Error, "src/watch.ts:182:35")
184
186
  onPush?.({ path, users: [], characterCount: 0, error })
185
187
  return
186
188
  }
@@ -195,7 +197,7 @@ async function watch(
195
197
  try {
196
198
  await writeFile(typeDeclarationPath, typeDeclaration)
197
199
  } catch (error) {
198
- assert(error instanceof Error, "src/watch.ts:210:35")
200
+ assert(error instanceof Error, "src/watch.ts:215:35")
199
201
  if ("EISDIR" != error.code) throw error
200
202
  typeDeclarationPath = resolve(typeDeclarationPath, "player.d.ts")
201
203
  await writeFile(typeDeclarationPath, typeDeclaration)