hackmud-script-manager 0.20.4-8b61cf2 → 0.20.4-8b994a4
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 +302 -271
- package/env.d.ts +325 -317
- 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 +13 -10
- package/processScript/minify.js +5 -14
- package/processScript/preprocess.js +7 -5
- package/processScript/transform.d.ts +3 -0
- package/processScript/transform.js +103 -86
- package/push.d.ts +2 -1
- package/push.js +13 -11
- 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-8b61cf2",
|
|
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,162 +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.20.4-8b994a4")
|
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
|
-
|
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
|
105
|
+
process.exit(1)
|
152
106
|
}
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
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")
|
160
118
|
logHelp()
|
161
|
-
|
119
|
+
process.exit(1)
|
162
120
|
}
|
163
|
-
|
164
|
-
|
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) {
|
121
|
+
const fileExtension = extname(target)
|
122
|
+
if (!supportedExtensions.includes(fileExtension)) {
|
176
123
|
logError(
|
177
|
-
`
|
124
|
+
`Unsupported file extension "${chalk.bold(fileExtension)}"\nSupported extensions are "${supportedExtensions.map(extension => chalk.bold(extension)).join('", "')}"`
|
178
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"
|
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`)
|
179
187
|
logHelp()
|
180
|
-
|
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))
|
181
277
|
}
|
182
|
-
shouldMinify = !shouldSkipMinify
|
183
|
-
}
|
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
278
|
}
|
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
279
|
}
|
217
280
|
break
|
218
281
|
case "pull":
|
@@ -222,8 +285,9 @@ switch (commands[0]) {
|
|
222
285
|
if (!script) {
|
223
286
|
logError("Must provide the script to pull\n")
|
224
287
|
logHelp()
|
225
|
-
|
288
|
+
process.exit(1)
|
226
289
|
}
|
290
|
+
complainAboutUnrecognisedOptions()
|
227
291
|
const sourcePath = commands[2] || "."
|
228
292
|
await pull(sourcePath, hackmudPath, script).catch(error => {
|
229
293
|
console.error(error)
|
@@ -233,8 +297,9 @@ switch (commands[0]) {
|
|
233
297
|
break
|
234
298
|
case "sync-macros":
|
235
299
|
{
|
236
|
-
const hackmudPath = getHackmudPath()
|
237
|
-
|
300
|
+
const hackmudPath = getHackmudPath()
|
301
|
+
complainAboutUnrecognisedOptions()
|
302
|
+
const { macrosSynced, usersSynced } = await syncMacros(hackmudPath)
|
238
303
|
log(`Synced ${macrosSynced} macros to ${usersSynced} users`)
|
239
304
|
}
|
240
305
|
break
|
@@ -242,19 +307,30 @@ switch (commands[0]) {
|
|
242
307
|
case "gen-type-declaration":
|
243
308
|
case "gen-dts":
|
244
309
|
case "gen-types":
|
310
|
+
case "emit-dts":
|
245
311
|
{
|
246
|
-
|
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]
|
247
322
|
if (!target) {
|
248
323
|
logError("Must provide target directory\n")
|
249
324
|
logHelp()
|
250
|
-
|
325
|
+
process.exit(1)
|
251
326
|
}
|
327
|
+
complainAboutUnrecognisedOptions()
|
252
328
|
const sourcePath = resolve(target),
|
253
329
|
outputPath = commands[2] || "./player.d.ts",
|
254
|
-
typeDeclaration = await generateTypeDeclaration(sourcePath,
|
330
|
+
typeDeclaration = await generateTypeDeclaration(sourcePath, hackmudPath)
|
255
331
|
let typeDeclarationPath = resolve(outputPath)
|
256
332
|
await writeFile(typeDeclarationPath, typeDeclaration).catch(error => {
|
257
|
-
assert(error instanceof Error, "src/bin/hsm.ts:
|
333
|
+
assert(error instanceof Error, "src/bin/hsm.ts:428:35")
|
258
334
|
if ("EISDIR" != error.code) throw error
|
259
335
|
typeDeclarationPath = resolve(typeDeclarationPath, "player.d.ts")
|
260
336
|
return writeFile(typeDeclarationPath, typeDeclaration)
|
@@ -263,102 +339,8 @@ switch (commands[0]) {
|
|
263
339
|
}
|
264
340
|
break
|
265
341
|
case "help":
|
266
|
-
case "h":
|
267
342
|
logHelp()
|
268
343
|
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
344
|
default:
|
363
345
|
commands[0] && logError(`Unknown command: ${colourL(commands[0])}\n`)
|
364
346
|
logHelp()
|
@@ -366,23 +348,21 @@ switch (commands[0]) {
|
|
366
348
|
autoExit && process.exit()
|
367
349
|
function logHelp() {
|
368
350
|
const pushCommandDescription = "Push scripts from a directory to hackmud user's scripts directories",
|
369
|
-
forceQuineCheatsOptionDescription = `Force quine cheats on. Use ${colourN("--force-quine-cheats")}=${colourV("false")} to force off`,
|
370
351
|
hackmudPathOption = `${colourN("--hackmud-path")}=${colourB("<path>")}\n Override hackmud path`
|
371
|
-
console.log(colourN("Version") + colourS(": ") + colourV(version))
|
372
352
|
switch (commands[0]) {
|
373
353
|
case "dev":
|
374
354
|
case "watch":
|
375
355
|
case "push":
|
376
356
|
console.log(
|
377
357
|
colourS(
|
378
|
-
|
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`
|
379
359
|
)
|
380
360
|
)
|
381
361
|
break
|
382
362
|
case "pull":
|
383
363
|
console.log(
|
384
364
|
colourS(
|
385
|
-
|
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}`
|
386
366
|
)
|
387
367
|
)
|
388
368
|
break
|
@@ -390,7 +370,7 @@ function logHelp() {
|
|
390
370
|
case "golf":
|
391
371
|
console.log(
|
392
372
|
colourS(
|
393
|
-
|
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`
|
394
374
|
)
|
395
375
|
)
|
396
376
|
break
|
@@ -398,6 +378,15 @@ function logHelp() {
|
|
398
378
|
case "gen-type-declaration":
|
399
379
|
case "gen-dts":
|
400
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
|
+
)
|
401
390
|
console.log(
|
402
391
|
colourS(
|
403
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}`
|
@@ -407,40 +396,82 @@ function logHelp() {
|
|
407
396
|
case "sync-macros":
|
408
397
|
console.log(
|
409
398
|
colourS(
|
410
|
-
|
399
|
+
`${colourJ("Sync macros across all hackmud users")}\n\n${colourA("Options:")}\n${hackmudPathOption}`
|
411
400
|
)
|
412
401
|
)
|
413
402
|
break
|
414
403
|
default:
|
415
404
|
console.log(
|
416
405
|
colourS(
|
417
|
-
|
406
|
+
`${colourJ("Hackmud Script Manager")}\n${colourN("Version") + colourS(": ") + colourV("0.20.4-8b994a4")}\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`
|
418
407
|
)
|
419
408
|
)
|
420
409
|
}
|
421
410
|
}
|
422
|
-
function logInfo({ path, users, characterCount, error }, hackmudPath) {
|
411
|
+
function logInfo({ path, users, characterCount, error, warnings }, hackmudPath) {
|
423
412
|
path = relative(".", path)
|
424
|
-
error
|
425
|
-
|
426
|
-
|
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(
|
427
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")}`
|
428
418
|
)
|
419
|
+
}
|
429
420
|
}
|
430
421
|
function logError(message) {
|
431
422
|
console.error(colourD(message))
|
432
423
|
process.exitCode = 1
|
433
424
|
}
|
434
425
|
function getHackmudPath() {
|
435
|
-
const hackmudPathOption =
|
436
|
-
if (
|
437
|
-
|
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`)
|
438
453
|
logHelp()
|
439
454
|
process.exit(1)
|
440
455
|
}
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
)
|
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
|
+
}
|
446
477
|
}
|