hackmud-script-manager 0.20.5-97101bc → 0.21.0
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 +6 -5
- package/bin/hsm.js +302 -262
- package/env.d.ts +364 -275
- 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 +115 -97
- package/push.d.ts +10 -1
- package/push.js +40 -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.5-97101bc",
|
|
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,227 @@ const pushModule = import("../push.js"),
|
|
57
55
|
colourS = chalk.rgb(122, 178, 244),
|
58
56
|
colourV = chalk.rgb(255, 0, 236),
|
59
57
|
colourW = chalk.rgb(255, 150, 224)
|
60
|
-
|
58
|
+
process.version.startsWith("v21.") &&
|
59
|
+
console.warn(
|
60
|
+
colourF(
|
61
|
+
`${chalk.bold("Warning:")} Support for Node.js 21 will be dropped in the next minor version of HSM\n Your current version of Node.js is ${chalk.bold(process.version)}\n You should update your version of Node.js\n https://nodejs.org/en/download/package-manager\n`
|
62
|
+
)
|
63
|
+
)
|
64
|
+
if ("v" == commands[0] || "version" == commands[0] || popOption("version", "v")?.value) {
|
65
|
+
console.log("0.21.0")
|
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
|
+
`${chalk.bold("Warning:")} ${formatOption(noMinifyOption.name)} is deprecated and will be removed in the next minor\n release of HSM\n You should switch to using its alias ${colourN("--no-minify")}\n`
|
100
87
|
)
|
101
|
-
|
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)
|
162
|
+
console.warn(colourF(`${chalk.bold("Warning:")} ${message}`))
|
163
|
+
await writeFilePersistent(outputPath, script)
|
164
|
+
.catch(error => {
|
165
|
+
if (!commands[2] || "EISDIR" != error.code) throw error
|
166
|
+
outputPath = resolve(outputPath, basename(target, fileExtension) + ".js")
|
167
|
+
return writeFilePersistent(outputPath, script)
|
168
|
+
})
|
169
|
+
.then(() =>
|
170
|
+
log(
|
171
|
+
`Wrote ${chalk.bold(countHackmudCharacters(script))} chars to ${chalk.bold(relative(".", outputPath))} | took ${Math.round(100 * timeTook) / 100}ms`
|
172
|
+
)
|
173
|
+
)
|
174
|
+
})
|
175
|
+
if (watchOption) {
|
176
|
+
const { watch: watchFile } = await chokidarModule
|
177
|
+
watchFile(target, { awaitWriteFinish: { stabilityThreshold: 100 } })
|
178
|
+
.on("ready", () => log("Watching " + target))
|
179
|
+
.on("change", golfFile)
|
180
|
+
autoExit = !1
|
181
|
+
} else await golfFile()
|
182
|
+
} else {
|
183
|
+
const hackmudPath = getHackmudPath(),
|
184
|
+
sourcePath = commands[1]
|
185
|
+
if (!sourcePath) {
|
186
|
+
logError(`Must provide the directory to ${"push" == commands[0] ? "push from" : "watch"}\n`)
|
170
187
|
logHelp()
|
171
|
-
|
188
|
+
process.exit(1)
|
189
|
+
}
|
190
|
+
const scripts = commands.slice(2)
|
191
|
+
if (scripts.length) {
|
192
|
+
const invalidScript = scripts.find(
|
193
|
+
script => !/^(?:[a-z_][a-z\d_]{0,24}|\*)\.(?:[a-z_][a-z\d_]{0,24}|\*)$/.test(script)
|
194
|
+
)
|
195
|
+
if (invalidScript) {
|
196
|
+
logError(`Invalid script name: ${JSON.stringify(invalidScript)}\n`)
|
197
|
+
logHelp()
|
198
|
+
process.exit(1)
|
199
|
+
}
|
200
|
+
} else scripts.push("*.*")
|
201
|
+
const watchOption = popOption("watch")
|
202
|
+
if ("push" != commands[0] || watchOption?.value) {
|
203
|
+
const dtsPathOption = popOption(
|
204
|
+
"dts-path",
|
205
|
+
"type-declaration-path",
|
206
|
+
"type-declaration",
|
207
|
+
"dts",
|
208
|
+
"gen-types"
|
209
|
+
)
|
210
|
+
dtsPathOption &&
|
211
|
+
"dts-path" != dtsPathOption.name &&
|
212
|
+
"type-declaration-path" != dtsPathOption.name &&
|
213
|
+
console.warn(
|
214
|
+
colourF(
|
215
|
+
`${chalk.bold("Warning:")} ${formatOption(dtsPathOption.name)} is deprecated and will be removed in the\n next minor release of HSM\n You should switch to using its alias ${colourN("--dts-path")}\n`
|
216
|
+
)
|
217
|
+
)
|
218
|
+
complainAboutUnrecognisedOptions()
|
219
|
+
const { watch } = await watchModule
|
220
|
+
watch(sourcePath, hackmudPath, {
|
221
|
+
scripts,
|
222
|
+
onPush: info => logInfo(info, hackmudPath),
|
223
|
+
typeDeclarationPath: dtsPathOption?.value.toString(),
|
224
|
+
minify: noMinifyOption && !noMinifyOption.value,
|
225
|
+
mangleNames: mangleNamesOption?.value,
|
226
|
+
onReady: () => log("Watching"),
|
227
|
+
forceQuineCheats: forceQuineCheatsOption?.value ?? !noQuineCheatsOptions?.value,
|
228
|
+
rootFolderPath
|
229
|
+
})
|
230
|
+
autoExit = !1
|
231
|
+
} else {
|
232
|
+
const dtsPathOption = popOption("dts-path")
|
233
|
+
complainAboutUnrecognisedOptions()
|
234
|
+
let declarationPathPromise
|
235
|
+
if (dtsPathOption) {
|
236
|
+
if ("string" != typeof dtsPathOption.value) {
|
237
|
+
logError(
|
238
|
+
`Option ${formatOption(dtsPathOption.name)} must be a string, got ${colourV(dtsPathOption.value)}\n`
|
239
|
+
)
|
240
|
+
logHelp()
|
241
|
+
process.exit(1)
|
242
|
+
}
|
243
|
+
let typeDeclarationPath = resolve(dtsPathOption.value)
|
244
|
+
const typeDeclaration = await generateTypeDeclaration(sourcePath, hackmudPath)
|
245
|
+
declarationPathPromise = writeFile(typeDeclarationPath, typeDeclaration)
|
246
|
+
.catch(error => {
|
247
|
+
assert(error instanceof Error, "src/bin/hsm.ts:292:38")
|
248
|
+
if ("EISDIR" != error.code) throw error
|
249
|
+
typeDeclarationPath = resolve(typeDeclarationPath, "player.d.ts")
|
250
|
+
return writeFile(typeDeclarationPath, typeDeclaration)
|
251
|
+
})
|
252
|
+
.then(() => typeDeclarationPath)
|
253
|
+
}
|
254
|
+
const { push, MissingSourceFolderError, MissingHackmudFolderError, NoUsersError } =
|
255
|
+
await pushModule,
|
256
|
+
infos = await push(sourcePath, hackmudPath, {
|
257
|
+
scripts,
|
258
|
+
onPush: info => logInfo(info, hackmudPath),
|
259
|
+
minify: noMinifyOption && !noMinifyOption.value,
|
260
|
+
mangleNames: mangleNamesOption?.value,
|
261
|
+
forceQuineCheats: forceQuineCheatsOption?.value ?? !noQuineCheatsOptions?.value,
|
262
|
+
rootFolderPath
|
263
|
+
})
|
264
|
+
if (infos instanceof Error) {
|
265
|
+
logError(infos.message)
|
266
|
+
if (infos instanceof MissingSourceFolderError || infos instanceof NoUsersError) {
|
267
|
+
console.log()
|
268
|
+
logHelp()
|
269
|
+
} else
|
270
|
+
infos instanceof MissingHackmudFolderError &&
|
271
|
+
log(
|
272
|
+
`If this is not where your hackmud folder is, you can specify it with the\n${colourN("--hackmud-path")}=${colourB("<path>")} option or ${colourN("HSM_HACKMUD_PATH")} environment variable`
|
273
|
+
)
|
274
|
+
} else infos.length || logError("Could not find any scripts to push")
|
275
|
+
declarationPathPromise &&
|
276
|
+
log("Wrote type declaration to " + chalk.bold(await declarationPathPromise))
|
172
277
|
}
|
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
278
|
}
|
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
279
|
}
|
208
280
|
break
|
209
281
|
case "pull":
|
@@ -213,8 +285,9 @@ switch (commands[0]) {
|
|
213
285
|
if (!script) {
|
214
286
|
logError("Must provide the script to pull\n")
|
215
287
|
logHelp()
|
216
|
-
|
288
|
+
process.exit(1)
|
217
289
|
}
|
290
|
+
complainAboutUnrecognisedOptions()
|
218
291
|
const sourcePath = commands[2] || "."
|
219
292
|
await pull(sourcePath, hackmudPath, script).catch(error => {
|
220
293
|
console.error(error)
|
@@ -224,8 +297,9 @@ switch (commands[0]) {
|
|
224
297
|
break
|
225
298
|
case "sync-macros":
|
226
299
|
{
|
227
|
-
const hackmudPath = getHackmudPath()
|
228
|
-
|
300
|
+
const hackmudPath = getHackmudPath()
|
301
|
+
complainAboutUnrecognisedOptions()
|
302
|
+
const { macrosSynced, usersSynced } = await syncMacros(hackmudPath)
|
229
303
|
log(`Synced ${macrosSynced} macros to ${usersSynced} users`)
|
230
304
|
}
|
231
305
|
break
|
@@ -233,19 +307,30 @@ switch (commands[0]) {
|
|
233
307
|
case "gen-type-declaration":
|
234
308
|
case "gen-dts":
|
235
309
|
case "gen-types":
|
310
|
+
case "emit-dts":
|
236
311
|
{
|
237
|
-
|
312
|
+
if ("emit-dts" != commands[0] && "gen-dts" != commands[0]) {
|
313
|
+
warnedDeprecatedEmitDtsAlias = !0
|
314
|
+
console.warn(
|
315
|
+
colourF(
|
316
|
+
`${chalk.bold("Warning:")} ${colourC("hsm")} ${colourL(commands[0])} is deprecated and will be removed\n in the next minor release of HSM\n You should switch to using its alias ${colourC("hsm")} ${colourL("emit-dts")}\n`
|
317
|
+
)
|
318
|
+
)
|
319
|
+
}
|
320
|
+
const hackmudPath = getHackmudPath(),
|
321
|
+
target = commands[1]
|
238
322
|
if (!target) {
|
239
323
|
logError("Must provide target directory\n")
|
240
324
|
logHelp()
|
241
|
-
|
325
|
+
process.exit(1)
|
242
326
|
}
|
327
|
+
complainAboutUnrecognisedOptions()
|
243
328
|
const sourcePath = resolve(target),
|
244
329
|
outputPath = commands[2] || "./player.d.ts",
|
245
|
-
typeDeclaration = await generateTypeDeclaration(sourcePath,
|
330
|
+
typeDeclaration = await generateTypeDeclaration(sourcePath, hackmudPath)
|
246
331
|
let typeDeclarationPath = resolve(outputPath)
|
247
332
|
await writeFile(typeDeclarationPath, typeDeclaration).catch(error => {
|
248
|
-
assert(error instanceof Error, "src/bin/hsm.ts:
|
333
|
+
assert(error instanceof Error, "src/bin/hsm.ts:428:35")
|
249
334
|
if ("EISDIR" != error.code) throw error
|
250
335
|
typeDeclarationPath = resolve(typeDeclarationPath, "player.d.ts")
|
251
336
|
return writeFile(typeDeclarationPath, typeDeclaration)
|
@@ -254,102 +339,8 @@ switch (commands[0]) {
|
|
254
339
|
}
|
255
340
|
break
|
256
341
|
case "help":
|
257
|
-
case "h":
|
258
342
|
logHelp()
|
259
343
|
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
344
|
default:
|
354
345
|
commands[0] && logError(`Unknown command: ${colourL(commands[0])}\n`)
|
355
346
|
logHelp()
|
@@ -357,23 +348,21 @@ switch (commands[0]) {
|
|
357
348
|
autoExit && process.exit()
|
358
349
|
function logHelp() {
|
359
350
|
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
351
|
hackmudPathOption = `${colourN("--hackmud-path")}=${colourB("<path>")}\n Override hackmud path`
|
362
|
-
console.log(colourN("Version") + colourS(": ") + colourV(version))
|
363
352
|
switch (commands[0]) {
|
364
353
|
case "dev":
|
365
354
|
case "watch":
|
366
355
|
case "push":
|
367
356
|
console.log(
|
368
357
|
colourS(
|
369
|
-
|
358
|
+
`${colourJ("push" == commands[0] ? pushCommandDescription : "Watch a directory and push a script when modified")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourB('<directory> ["<script user>.<script name>"...]')}\n\n${colourA("Arguments:")}\n${colourB("<directory>")}\n The source directory containing your scripts\n${colourB("<script user>")}\n A user to push script(s) to. Can be set to wild card (${colourV("*")}) which will try\n and discover users to push to\n${colourB("<script name>")}\n Name of a script to push. Can be set to wild card (${colourV("*")}) to find all scripts\n\n${colourA("Options:")}\n${colourN("--no-minify")}\n Skip minification to produce a "readable" script\n${colourN("--mangle-names")}\n Reduce character count further but lose function names in error call stacks\n${colourN("--force-quine-cheats")}, ${colourN("--no-quine-cheats")}\n Force quine cheats on or off\n${hackmudPathOption}\n${colourN("--dts-path")}=${colourB("<path>")}\n Path to generate a type declaration (.d.ts) file for the scripts\n${colourN("--watch")}\n Watch for changes\n${colourN("--root-folder-path")}\n The folder that root will be aliased to in import statements\n\n${colourA("Examples:")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourV("src")}\n Pushes all scripts found in ${colourV("src")} folder to all users\n${colourC("hsm")} ${colourL(commands[0])} ${colourV("src")} ${colourC("foo")}${colourV(".")}${colourL("bar")}\n Pushes a script named ${colourL("bar")} found in ${colourV("src")} folder to user ${userColours.get("foo")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourV("src")} ${colourC("foo")}${colourV(".")}${colourL("bar")} ${colourC("baz")}${colourV(".")}${colourL("qux")}\n Multiple can be specified\n${colourC("hsm")} ${colourL(commands[0])} ${colourV("src")} ${colourC("foo")}${colourV(".")}${colourL("*")}\n Pushes all scripts found in ${colourV("src")} folder to user ${userColours.get("foo")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourV("src")} ${colourC("*")}${colourV(".")}${colourL("foo")}\n Pushes all scripts named ${colourL("foo")} found in ${colourV("src")} folder to all user\n${colourC("hsm")} ${colourL(commands[0])} ${colourV("src")} ${colourC("*")}${colourV(".")}${colourL("*")}\n Pushes all scripts found in ${colourV("src")} folder to all users`
|
370
359
|
)
|
371
360
|
)
|
372
361
|
break
|
373
362
|
case "pull":
|
374
363
|
console.log(
|
375
364
|
colourS(
|
376
|
-
|
365
|
+
`${colourJ("Pull a script a from a hackmud user's script directory")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourB("<script user>")}${colourV(".")}${colourB("<script name>")}\n\n${colourA("Options:")}\n${hackmudPathOption}`
|
377
366
|
)
|
378
367
|
)
|
379
368
|
break
|
@@ -381,7 +370,7 @@ function logHelp() {
|
|
381
370
|
case "golf":
|
382
371
|
console.log(
|
383
372
|
colourS(
|
384
|
-
|
373
|
+
`${colourJ("Minify a script file on the spot")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourB("<target> [output path]")}\n\n${colourA("Options:")}\n${colourN("--no-minify")}\n Skip minification to produce a "readable" script\n${colourN("--mangle-names")}\n Reduce character count further but lose function names in error call stacks\n${colourN("--force-quine-cheats")}, ${colourN("--no-quine-cheats")}\n Force quine cheats on or off\n${colourN("--watch")}\n Watch for changes\n${colourN("--root-folder-path")}\n The folder that root will be aliased to in import statements`
|
385
374
|
)
|
386
375
|
)
|
387
376
|
break
|
@@ -389,6 +378,15 @@ function logHelp() {
|
|
389
378
|
case "gen-type-declaration":
|
390
379
|
case "gen-dts":
|
391
380
|
case "gen-types":
|
381
|
+
case "emit-dts":
|
382
|
+
warnedDeprecatedEmitDtsAlias ||
|
383
|
+
"emit-dts" == commands[0] ||
|
384
|
+
"gen-dts" == commands[0] ||
|
385
|
+
console.warn(
|
386
|
+
colourF(
|
387
|
+
`${chalk.bold("Warning:")} ${colourC("hsm")} ${colourL(commands[0])} is deprecated and will be removed\n in the next minor release of HSM\n You should switch to using its alias ${colourC("hsm")} ${colourL("emit-dts")}\n`
|
388
|
+
)
|
389
|
+
)
|
392
390
|
console.log(
|
393
391
|
colourS(
|
394
392
|
`${colourJ("Generate a type declaration file for a directory of scripts")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourB("<directory> [output path]")}\n\n${colourA("Options:")}\n${hackmudPathOption}`
|
@@ -398,40 +396,82 @@ function logHelp() {
|
|
398
396
|
case "sync-macros":
|
399
397
|
console.log(
|
400
398
|
colourS(
|
401
|
-
|
399
|
+
`${colourJ("Sync macros across all hackmud users")}\n\n${colourA("Options:")}\n${hackmudPathOption}`
|
402
400
|
)
|
403
401
|
)
|
404
402
|
break
|
405
403
|
default:
|
406
404
|
console.log(
|
407
405
|
colourS(
|
408
|
-
|
406
|
+
`${colourJ("Hackmud Script Manager")}\n${colourN("Version") + colourS(": ") + colourV("0.21.0")}\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
407
|
)
|
410
408
|
)
|
411
409
|
}
|
412
410
|
}
|
413
|
-
function logInfo({ path, users, characterCount, error }, hackmudPath) {
|
411
|
+
function logInfo({ path, users, characterCount, error, warnings }, hackmudPath) {
|
414
412
|
path = relative(".", path)
|
415
|
-
error
|
416
|
-
|
417
|
-
|
413
|
+
if (error) logError(`Error "${chalk.bold(error.message)}" in ${chalk.bold(path)}`)
|
414
|
+
else {
|
415
|
+
for (const warning of warnings) console.warn(colourF(`${chalk.bold("Warning:")} ${warning.message}`))
|
416
|
+
log(
|
418
417
|
`Pushed ${chalk.bold(path)} to ${users.map(user => chalk.bold(userColours.get(user))).join(", ")} | ${chalk.bold(characterCount + "")} chars | ${chalk.bold(resolve(hackmudPath, users[0], "scripts", basename(path, extname(path))) + ".js")}`
|
419
418
|
)
|
419
|
+
}
|
420
420
|
}
|
421
421
|
function logError(message) {
|
422
422
|
console.error(colourD(message))
|
423
423
|
process.exitCode = 1
|
424
424
|
}
|
425
425
|
function getHackmudPath() {
|
426
|
-
const hackmudPathOption =
|
427
|
-
if (
|
428
|
-
|
426
|
+
const hackmudPathOption = popOption("hackmud-path")
|
427
|
+
if (hackmudPathOption) {
|
428
|
+
if ("string" != typeof hackmudPathOption.value) {
|
429
|
+
logError(`Option ${colourN("--hackmud-path")} must be a string, got ${colourV(hackmudPathOption.value)}\n`)
|
430
|
+
logHelp()
|
431
|
+
process.exit(1)
|
432
|
+
}
|
433
|
+
if (!hackmudPathOption.value) {
|
434
|
+
logError(`Option ${colourN("--hackmud-path")} was specified but empty\n`)
|
435
|
+
logHelp()
|
436
|
+
process.exit(1)
|
437
|
+
}
|
438
|
+
return hackmudPathOption.value
|
439
|
+
}
|
440
|
+
if (null != process.env.HSM_HACKMUD_PATH) {
|
441
|
+
if (!process.env.HSM_HACKMUD_PATH) {
|
442
|
+
logError(`Environment variable ${colourN("HSM_HACKMUD_PATH")} was specified but empty\n`)
|
443
|
+
logHelp()
|
444
|
+
process.exit(1)
|
445
|
+
}
|
446
|
+
return process.env.HSM_HACKMUD_PATH
|
447
|
+
}
|
448
|
+
return "win32" == process.platform ? resolve(process.env.APPDATA, "hackmud") : resolve(homedir(), ".config/hackmud")
|
449
|
+
}
|
450
|
+
function assertOptionIsBoolean(option) {
|
451
|
+
if ("boolean" != typeof option.value) {
|
452
|
+
logError(`The value for ${formatOption(option.name)} must be ${colourV("true")} or ${colourV("false")}\n`)
|
429
453
|
logHelp()
|
430
454
|
process.exit(1)
|
431
455
|
}
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
)
|
456
|
+
}
|
457
|
+
function popOption(...names) {
|
458
|
+
const presentOptionNames = names.filter(name => options.has(name))
|
459
|
+
if (!presentOptionNames.length) return
|
460
|
+
if (presentOptionNames.length > 1) {
|
461
|
+
logError(
|
462
|
+
`The options ${presentOptionNames.map(formatOption).join(", ")} are aliases for each other. Please only specify one`
|
463
|
+
)
|
464
|
+
process.exit(1)
|
465
|
+
}
|
466
|
+
const value = options.get(presentOptionNames[0])
|
467
|
+
options.delete(presentOptionNames[0])
|
468
|
+
return { name: presentOptionNames[0], value }
|
469
|
+
}
|
470
|
+
function complainAboutUnrecognisedOptions() {
|
471
|
+
if (options.size) {
|
472
|
+
logError(
|
473
|
+
`Unrecognised option${options.size > 1 ? "s" : ""}: ${[...options.keys()].map(formatOption).join(", ")}`
|
474
|
+
)
|
475
|
+
process.exit(1)
|
476
|
+
}
|
437
477
|
}
|