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