hackmud-script-manager 0.13.0-a60a7a2 → 0.13.0-c461329
Sign up to get free protection for your applications and to get access to all the features.
- package/.gitattributes +1 -0
- package/.github/workflows/codeql-analysis.yml +39 -0
- package/.github/workflows/publish.yml +42 -0
- package/.vscode/settings.json +6 -0
- package/babel.config.json +6 -0
- package/package.json +12 -21
- package/rollup.config.js +110 -0
- package/scripts/build-package-json.js +36 -0
- package/scripts/jsconfig.json +5 -0
- package/scripts/version-dev.js +25 -0
- package/src/bin/hsm.ts +505 -0
- package/src/constants.json +3 -0
- package/src/generateTypings.ts +116 -0
- package/src/index.ts +19 -0
- package/src/modules.d.ts +5 -0
- package/src/processScript/index.ts +198 -0
- package/src/processScript/minify.ts +529 -0
- package/src/processScript/postprocess.ts +38 -0
- package/src/processScript/preprocess.ts +146 -0
- package/src/processScript/transform.ts +760 -0
- package/src/pull.ts +16 -0
- package/src/push.ts +314 -0
- package/src/syncMacros.ts +52 -0
- package/src/test.ts +59 -0
- package/src/tsconfig.json +20 -0
- package/src/watch.ts +156 -0
- package/tsconfig.json +12 -0
- package/assert-1b7dada8.js +0 -1
- package/bin/hsm.d.ts +0 -2
- package/bin/hsm.js +0 -2
- package/generateTypings.d.ts +0 -2
- package/generateTypings.js +0 -1
- package/index.d.ts +0 -15
- package/index.js +0 -1
- package/processScript/compile.d.ts +0 -17
- package/processScript/compile.js +0 -1
- package/processScript/index.d.ts +0 -30
- package/processScript/index.js +0 -1
- package/processScript/minify.d.ts +0 -7
- package/processScript/minify.js +0 -1
- package/processScript/postProcess.d.ts +0 -2
- package/processScript/postProcess.js +0 -1
- package/processScript/preProcess.d.ts +0 -15
- package/processScript/preProcess.js +0 -1
- package/pull.d.ts +0 -9
- package/pull.js +0 -1
- package/push.d.ts +0 -26
- package/push.js +0 -1
- package/spliceString-2c6f214f.js +0 -1
- package/syncMacros.d.ts +0 -5
- package/syncMacros.js +0 -1
- package/test.d.ts +0 -6
- package/test.js +0 -1
- package/watch.d.ts +0 -14
- package/watch.js +0 -1
package/src/pull.ts
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
import { copyFilePersistent } from "@samual/lib"
|
2
|
+
import { resolve as resolvePath } from "path"
|
3
|
+
|
4
|
+
/**
|
5
|
+
* Copies script from hackmud to local source folder.
|
6
|
+
*
|
7
|
+
* @param sourceFolderPath path to folder containing source files
|
8
|
+
* @param hackmudPath path to hackmud directory
|
9
|
+
* @param script script to pull in `user.name` format
|
10
|
+
*/
|
11
|
+
export async function pull(sourceFolderPath: string, hackmudPath: string, script: string) {
|
12
|
+
const [ user, name ] = script.split(".")
|
13
|
+
await copyFilePersistent(resolvePath(hackmudPath, user, "scripts", `${name}.js`), resolvePath(sourceFolderPath, user, `${name}.js`))
|
14
|
+
}
|
15
|
+
|
16
|
+
export default pull
|
package/src/push.ts
ADDED
@@ -0,0 +1,314 @@
|
|
1
|
+
import { countHackmudCharacters, DynamicMap, forEachParallel, writeFilePersistent } from "@samual/lib"
|
2
|
+
import fs from "fs"
|
3
|
+
import { basename as getBaseName, extname as getFileExtension, resolve as resolvePath } from "path"
|
4
|
+
import type { Info } from "."
|
5
|
+
import { supportedExtensions } from "./constants.json"
|
6
|
+
import processScript from "./processScript"
|
7
|
+
|
8
|
+
const { readFile, readdir: readDirectory } = fs.promises
|
9
|
+
|
10
|
+
interface PushOptions {
|
11
|
+
/**
|
12
|
+
* array of scripts in the format `foo.bar`
|
13
|
+
*
|
14
|
+
* also accepts wild card e.g. `*.bar` or `foo.*`
|
15
|
+
*
|
16
|
+
* pushes everything by default
|
17
|
+
*/
|
18
|
+
scripts: string | string[]
|
19
|
+
|
20
|
+
/** callback when a script is pushed */
|
21
|
+
onPush: (info: Info) => void
|
22
|
+
|
23
|
+
/** whether to do the minify step (defaults to `true`) */
|
24
|
+
minify: boolean
|
25
|
+
|
26
|
+
/** whether to mangle function and class names (defaults to `false`) */
|
27
|
+
mangleNames: boolean
|
28
|
+
}
|
29
|
+
|
30
|
+
/**
|
31
|
+
* Push scripts from a source directory to the hackmud directory.
|
32
|
+
*
|
33
|
+
* Files directly in the source folder are pushed to all users
|
34
|
+
* @param sourceDirectory directory containing source code
|
35
|
+
* @param hackmudDirectory directory created by hackmud containing user data including scripts
|
36
|
+
* @param options {@link PushOptions details}
|
37
|
+
* @returns array of info on pushed scripts
|
38
|
+
*/
|
39
|
+
export async function push(
|
40
|
+
sourceDirectory: string,
|
41
|
+
hackmudDirectory: string,
|
42
|
+
{
|
43
|
+
scripts = "*.*",
|
44
|
+
onPush = (info: Info) => {},
|
45
|
+
minify = true,
|
46
|
+
mangleNames = false
|
47
|
+
}: Partial<PushOptions> = {}
|
48
|
+
) {
|
49
|
+
if (typeof scripts == "string")
|
50
|
+
scripts = [ scripts ]
|
51
|
+
|
52
|
+
const scriptNamesByUser = new DynamicMap((user: string) => new Set<string>())
|
53
|
+
const wildScriptUsers = new Set<string>()
|
54
|
+
const wildUserScripts = new Set<string>()
|
55
|
+
|
56
|
+
let pushEverything = false
|
57
|
+
|
58
|
+
for (const fullScriptName of scripts) {
|
59
|
+
const [ user, scriptName ] = fullScriptName.split(".")
|
60
|
+
|
61
|
+
if (!user || user == "*") {
|
62
|
+
if (!scriptName || scriptName == "*")
|
63
|
+
pushEverything = true
|
64
|
+
else
|
65
|
+
wildUserScripts.add(scriptName)
|
66
|
+
} else if (!scriptName || scriptName == "*")
|
67
|
+
wildScriptUsers.add(user)
|
68
|
+
else
|
69
|
+
scriptNamesByUser.get(user).add(scriptName)
|
70
|
+
}
|
71
|
+
|
72
|
+
const usersByGlobalScriptsToPush = new DynamicMap((user: string) => new Set<string>())
|
73
|
+
const allInfo: Info[] = []
|
74
|
+
const scriptNamesAlreadyPushedByUser = new DynamicMap((user: string) => new Set<string>())
|
75
|
+
|
76
|
+
let sourceDirectoryDirents
|
77
|
+
|
78
|
+
// *.bar
|
79
|
+
if (wildUserScripts.size || pushEverything) {
|
80
|
+
const hackmudDirectoryDirents = await readDirectory(resolvePath(hackmudDirectory), { withFileTypes: true })
|
81
|
+
|
82
|
+
const allUsers = new Set([
|
83
|
+
...(sourceDirectoryDirents = await readDirectory(resolvePath(sourceDirectory), { withFileTypes: true }))
|
84
|
+
.filter(dirent => dirent.isDirectory())
|
85
|
+
.map(dirent => dirent.name),
|
86
|
+
...hackmudDirectoryDirents
|
87
|
+
.filter(dirent => dirent.isDirectory())
|
88
|
+
.map(dirent => dirent.name),
|
89
|
+
...hackmudDirectoryDirents
|
90
|
+
.filter(dirent => dirent.isFile() && getFileExtension(dirent.name) == ".key")
|
91
|
+
.map(dirent => dirent.name.slice(0, -4)),
|
92
|
+
...scriptNamesByUser.keys(),
|
93
|
+
...wildScriptUsers
|
94
|
+
])
|
95
|
+
|
96
|
+
if (pushEverything) {
|
97
|
+
for (const user of allUsers)
|
98
|
+
wildScriptUsers.add(user)
|
99
|
+
} else {
|
100
|
+
for (const user of allUsers) {
|
101
|
+
const scriptNames = scriptNamesByUser.get(user)
|
102
|
+
|
103
|
+
for (const scriptName of wildUserScripts)
|
104
|
+
scriptNames.add(scriptName)
|
105
|
+
}
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
// foo.*
|
110
|
+
await forEachParallel(wildScriptUsers, async user => {
|
111
|
+
await readDirectory(resolvePath(sourceDirectory, user), { withFileTypes: true }).then(async dirents => {
|
112
|
+
await forEachParallel(dirents, async dirent => {
|
113
|
+
const extension = getFileExtension(dirent.name)
|
114
|
+
|
115
|
+
if (dirent.isFile() && supportedExtensions.includes(extension)) {
|
116
|
+
const scriptName = getBaseName(dirent.name, extension)
|
117
|
+
const filePath = resolvePath(sourceDirectory, user, dirent.name)
|
118
|
+
|
119
|
+
const { srcLength, script: minifiedCode } = await processScript(
|
120
|
+
await readFile(filePath, { encoding: "utf-8" }),
|
121
|
+
{
|
122
|
+
minify,
|
123
|
+
scriptUser: user,
|
124
|
+
scriptName,
|
125
|
+
filePath,
|
126
|
+
mangleNames
|
127
|
+
}
|
128
|
+
)
|
129
|
+
|
130
|
+
const info: Info = {
|
131
|
+
file: `${user}/${dirent.name}`,
|
132
|
+
users: [ user ],
|
133
|
+
minLength: countHackmudCharacters(minifiedCode),
|
134
|
+
error: null,
|
135
|
+
srcLength
|
136
|
+
}
|
137
|
+
|
138
|
+
scriptNamesAlreadyPushedByUser.get(user).add(scriptName)
|
139
|
+
allInfo.push(info)
|
140
|
+
|
141
|
+
await writeFilePersistent(resolvePath(hackmudDirectory, user, `scripts/${scriptName}.js`), minifiedCode)
|
142
|
+
|
143
|
+
onPush(info)
|
144
|
+
}
|
145
|
+
})
|
146
|
+
}, (error: NodeJS.ErrnoException) => {
|
147
|
+
if (error.code != "ENOENT")
|
148
|
+
throw error
|
149
|
+
})
|
150
|
+
})
|
151
|
+
|
152
|
+
// foo.bar
|
153
|
+
await forEachParallel(scriptNamesByUser, async ([ user, scripts ]) => {
|
154
|
+
if (wildScriptUsers.has(user))
|
155
|
+
return
|
156
|
+
|
157
|
+
await forEachParallel(scripts, async scriptName => {
|
158
|
+
let code
|
159
|
+
let fileName
|
160
|
+
|
161
|
+
let filePath!: string
|
162
|
+
|
163
|
+
for (const extension of supportedExtensions) {
|
164
|
+
try {
|
165
|
+
fileName = `${scriptName}${extension}`
|
166
|
+
code = await readFile(filePath = resolvePath(sourceDirectory, user, fileName), { encoding: "utf-8" })
|
167
|
+
break
|
168
|
+
} catch {}
|
169
|
+
}
|
170
|
+
|
171
|
+
if (code) {
|
172
|
+
const { srcLength, script: minifiedCode } = await processScript(
|
173
|
+
code,
|
174
|
+
{
|
175
|
+
minify,
|
176
|
+
scriptUser: user,
|
177
|
+
scriptName,
|
178
|
+
filePath,
|
179
|
+
mangleNames
|
180
|
+
}
|
181
|
+
)
|
182
|
+
|
183
|
+
const info: Info = {
|
184
|
+
file: `${user}/${fileName}`,
|
185
|
+
users: [ user ],
|
186
|
+
minLength: countHackmudCharacters(minifiedCode),
|
187
|
+
error: null,
|
188
|
+
srcLength
|
189
|
+
}
|
190
|
+
|
191
|
+
allInfo.push(info)
|
192
|
+
|
193
|
+
await writeFilePersistent(resolvePath(hackmudDirectory, user, "scripts", `${scriptName}.js`), minifiedCode)
|
194
|
+
|
195
|
+
onPush(info)
|
196
|
+
} else
|
197
|
+
usersByGlobalScriptsToPush.get(scriptName).add(user)
|
198
|
+
})
|
199
|
+
})
|
200
|
+
|
201
|
+
// foo.* (global)
|
202
|
+
if (wildScriptUsers.size) {
|
203
|
+
await forEachParallel(sourceDirectoryDirents || await readDirectory(resolvePath(sourceDirectory), { withFileTypes: true }), async dirent => {
|
204
|
+
const extension = getFileExtension(dirent.name)
|
205
|
+
|
206
|
+
if (!dirent.isFile() || !supportedExtensions.includes(extension))
|
207
|
+
return
|
208
|
+
|
209
|
+
const scriptName = getBaseName(dirent.name, extension)
|
210
|
+
const usersToPushTo = [ ...wildScriptUsers, ...usersByGlobalScriptsToPush.get(scriptName) ].filter(user => !scriptNamesAlreadyPushedByUser.get(user).has(scriptName))
|
211
|
+
|
212
|
+
if (!usersToPushTo.length)
|
213
|
+
return
|
214
|
+
|
215
|
+
const uniqueID = Math.floor(Math.random() * (2 ** 52)).toString(36).padStart(11, "0")
|
216
|
+
const filePath = resolvePath(sourceDirectory, dirent.name)
|
217
|
+
|
218
|
+
const { srcLength, script: minifiedCode } = await processScript(
|
219
|
+
await readFile(filePath, { encoding: "utf-8" }),
|
220
|
+
{
|
221
|
+
minify,
|
222
|
+
scriptUser: true,
|
223
|
+
scriptName,
|
224
|
+
uniqueID,
|
225
|
+
filePath,
|
226
|
+
mangleNames
|
227
|
+
}
|
228
|
+
)
|
229
|
+
|
230
|
+
const info: Info = {
|
231
|
+
file: dirent.name,
|
232
|
+
users: usersToPushTo,
|
233
|
+
minLength: countHackmudCharacters(minifiedCode),
|
234
|
+
error: null,
|
235
|
+
srcLength
|
236
|
+
}
|
237
|
+
|
238
|
+
await forEachParallel(usersToPushTo, user =>
|
239
|
+
writeFilePersistent(
|
240
|
+
resolvePath(
|
241
|
+
hackmudDirectory,
|
242
|
+
user,
|
243
|
+
`scripts/${scriptName}.js`
|
244
|
+
),
|
245
|
+
minifiedCode
|
246
|
+
.replace(new RegExp(`$${uniqueID}$SCRIPT_USER`, "g"), user)
|
247
|
+
.replace(new RegExp(`$${uniqueID}$FULL_SCRIPT_NAME`, "g"), `${user}.${scriptName}`)
|
248
|
+
)
|
249
|
+
)
|
250
|
+
|
251
|
+
allInfo.push(info)
|
252
|
+
onPush(info)
|
253
|
+
})
|
254
|
+
} else {
|
255
|
+
// foo.bar (global)
|
256
|
+
await forEachParallel(usersByGlobalScriptsToPush, async ([ scriptName, users ]) => {
|
257
|
+
let code
|
258
|
+
let fileName!: string
|
259
|
+
let filePath!: string
|
260
|
+
|
261
|
+
for (const extension of supportedExtensions) {
|
262
|
+
try {
|
263
|
+
fileName = `${scriptName}${extension}`
|
264
|
+
code = await readFile(filePath = resolvePath(sourceDirectory, fileName), { encoding: "utf-8" })
|
265
|
+
break
|
266
|
+
} catch {}
|
267
|
+
}
|
268
|
+
|
269
|
+
if (code) {
|
270
|
+
const uniqueID = Math.floor(Math.random() * (2 ** 52)).toString(36).padStart(11, "0")
|
271
|
+
|
272
|
+
const { srcLength, script: minifiedCode } = await processScript(
|
273
|
+
code,
|
274
|
+
{
|
275
|
+
minify,
|
276
|
+
scriptUser: true,
|
277
|
+
scriptName,
|
278
|
+
uniqueID,
|
279
|
+
filePath,
|
280
|
+
mangleNames
|
281
|
+
}
|
282
|
+
)
|
283
|
+
|
284
|
+
const info: Info = {
|
285
|
+
file: fileName,
|
286
|
+
users: [ ...users ],
|
287
|
+
minLength: countHackmudCharacters(minifiedCode),
|
288
|
+
error: null,
|
289
|
+
srcLength
|
290
|
+
}
|
291
|
+
|
292
|
+
await forEachParallel(users, user =>
|
293
|
+
writeFilePersistent(
|
294
|
+
resolvePath(
|
295
|
+
hackmudDirectory,
|
296
|
+
user,
|
297
|
+
`scripts/${scriptName}.js`
|
298
|
+
),
|
299
|
+
minifiedCode
|
300
|
+
.replace(new RegExp(`$${uniqueID}$SCRIPT_USER`, "g"), user)
|
301
|
+
.replace(new RegExp(`$${uniqueID}$FULL_SCRIPT_NAME`, "g"), `${user}.${scriptName}`)
|
302
|
+
)
|
303
|
+
)
|
304
|
+
|
305
|
+
allInfo.push(info)
|
306
|
+
onPush(info)
|
307
|
+
}
|
308
|
+
})
|
309
|
+
}
|
310
|
+
|
311
|
+
return allInfo
|
312
|
+
}
|
313
|
+
|
314
|
+
export default push
|
@@ -0,0 +1,52 @@
|
|
1
|
+
import fs from "fs"
|
2
|
+
import { basename as getBaseName, extname as getFileExtension, resolve as resolvePath } from "path"
|
3
|
+
|
4
|
+
const { readFile, readdir: readDirectory, stat: getFileStatus, writeFile } = fs.promises
|
5
|
+
|
6
|
+
export async function syncMacros(hackmudPath: string) {
|
7
|
+
const files = await readDirectory(hackmudPath, { withFileTypes: true })
|
8
|
+
const macros = new Map<string, { macro: string, date: Date }>()
|
9
|
+
const users: string[] = []
|
10
|
+
|
11
|
+
for (const file of files) {
|
12
|
+
if (!file.isFile())
|
13
|
+
continue
|
14
|
+
|
15
|
+
switch (getFileExtension(file.name)) {
|
16
|
+
case ".macros": {
|
17
|
+
const lines = (await readFile(resolvePath(hackmudPath, file.name), { encoding: "utf-8" })).split("\n")
|
18
|
+
const date = (await getFileStatus(resolvePath(hackmudPath, file.name))).mtime
|
19
|
+
|
20
|
+
for (let i = 0; i < lines.length / 2 - 1; i++) {
|
21
|
+
const macroName = lines[i * 2]
|
22
|
+
const curMacro = macros.get(macroName)
|
23
|
+
|
24
|
+
if (!curMacro || date > curMacro.date)
|
25
|
+
macros.set(macroName, { date, macro: lines[i * 2 + 1] })
|
26
|
+
}
|
27
|
+
} break
|
28
|
+
|
29
|
+
case ".key": {
|
30
|
+
users.push(getBaseName(file.name, ".key"))
|
31
|
+
} break
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
let macroFile = ""
|
36
|
+
let macrosSynced = 0
|
37
|
+
|
38
|
+
for (const [ name, { macro } ] of [ ...macros ].sort(([ a ], [ b ]) => (a as any > b as any) - (a as any < b as any))) {
|
39
|
+
if (macro[0] != macro[0].toLowerCase())
|
40
|
+
continue
|
41
|
+
|
42
|
+
macroFile += `${name}\n${macro}\n`
|
43
|
+
macrosSynced++
|
44
|
+
}
|
45
|
+
|
46
|
+
for (const user of users)
|
47
|
+
writeFile(resolvePath(hackmudPath, user + ".macros"), macroFile)
|
48
|
+
|
49
|
+
return { macrosSynced, usersSynced: users.length }
|
50
|
+
}
|
51
|
+
|
52
|
+
export default syncMacros
|
package/src/test.ts
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
import fs from "fs"
|
2
|
+
import { extname as getFileExtension, resolve as resolvePath } from "path"
|
3
|
+
import { supportedExtensions } from "./constants.json"
|
4
|
+
import processScript from "./processScript"
|
5
|
+
|
6
|
+
const { readFile, readdir: readDirectory } = fs.promises
|
7
|
+
|
8
|
+
export async function test(srcPath: string) {
|
9
|
+
const promises: Promise<any>[] = []
|
10
|
+
|
11
|
+
const errors: {
|
12
|
+
file: string
|
13
|
+
message: string
|
14
|
+
line: number
|
15
|
+
}[] = []
|
16
|
+
|
17
|
+
for (const dirent of await readDirectory(srcPath, { withFileTypes: true })) {
|
18
|
+
if (dirent.isDirectory()) {
|
19
|
+
promises.push(readDirectory(resolvePath(srcPath, dirent.name), { withFileTypes: true }).then(files => {
|
20
|
+
const promises: Promise<any>[] = []
|
21
|
+
|
22
|
+
for (const file of files) {
|
23
|
+
if (!file.isFile() || !supportedExtensions.includes(getFileExtension(file.name)))
|
24
|
+
continue
|
25
|
+
|
26
|
+
promises.push(
|
27
|
+
readFile(resolvePath(srcPath, dirent.name, file.name), { encoding: "utf-8" })
|
28
|
+
.then(processScript)
|
29
|
+
.then(({ warnings }) =>
|
30
|
+
errors.push(...warnings.map(({ message, line }) => ({
|
31
|
+
file: `${dirent.name}/${file.name}`,
|
32
|
+
message, line
|
33
|
+
})))
|
34
|
+
)
|
35
|
+
)
|
36
|
+
}
|
37
|
+
|
38
|
+
return Promise.all(promises)
|
39
|
+
}))
|
40
|
+
} else if (dirent.isFile() && supportedExtensions.includes(getFileExtension(dirent.name))) {
|
41
|
+
promises.push(
|
42
|
+
readFile(resolvePath(srcPath, dirent.name), { encoding: "utf-8" })
|
43
|
+
.then(processScript)
|
44
|
+
.then(({ warnings }) =>
|
45
|
+
errors.push(...warnings.map(({ message, line }) => ({
|
46
|
+
file: dirent.name,
|
47
|
+
message, line
|
48
|
+
})))
|
49
|
+
)
|
50
|
+
)
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
await Promise.all(promises)
|
55
|
+
|
56
|
+
return errors
|
57
|
+
}
|
58
|
+
|
59
|
+
export default test
|
@@ -0,0 +1,20 @@
|
|
1
|
+
{
|
2
|
+
"compilerOptions": {
|
3
|
+
"target": "ES2019", /* ES2019 is oldest properly supported ES version in Node 12 which is the oldest currently supported Node version */
|
4
|
+
"module": "ES2020",
|
5
|
+
"strict": true,
|
6
|
+
"esModuleInterop": true,
|
7
|
+
"skipLibCheck": true,
|
8
|
+
"forceConsistentCasingInFileNames": true,
|
9
|
+
"declaration": true,
|
10
|
+
"outDir": "../dist",
|
11
|
+
"useUnknownInCatchVariables": true,
|
12
|
+
"exactOptionalPropertyTypes": true,
|
13
|
+
"noImplicitOverride": true,
|
14
|
+
"moduleResolution": "Node",
|
15
|
+
"resolveJsonModule": true,
|
16
|
+
"emitDeclarationOnly": true
|
17
|
+
},
|
18
|
+
"exclude": [ "bin" ],
|
19
|
+
"references": [ { "path": ".." } ]
|
20
|
+
}
|
package/src/watch.ts
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
import { countHackmudCharacters, writeFilePersistent } from "@samual/lib"
|
2
|
+
import { watch as watchDirectory } from "chokidar"
|
3
|
+
import fs from "fs"
|
4
|
+
import { basename as getBaseName, extname as getFileExtension, resolve as resolvePath } from "path"
|
5
|
+
import type { Info } from "."
|
6
|
+
import { supportedExtensions } from "./constants.json"
|
7
|
+
import generateTypings from "./generateTypings"
|
8
|
+
import processScript from "./processScript"
|
9
|
+
|
10
|
+
const { readFile, readdir: readDirectory } = fs.promises
|
11
|
+
|
12
|
+
/**
|
13
|
+
* Watches target file or folder for updates and builds and pushes updated file.
|
14
|
+
*
|
15
|
+
* @param srcDir path to folder containing source files
|
16
|
+
* @param hackmudDir path to hackmud directory
|
17
|
+
* @param users users to push to (pushes to all if empty)
|
18
|
+
* @param scripts scripts to push from (pushes from all if empty)
|
19
|
+
* @param onPush function that's called after each script has been built and written
|
20
|
+
*/
|
21
|
+
export function watch(srcDir: string, hackmudDir: string, users: string[], scripts: string[], onPush?: (info: Info) => void, { genTypes }: { genTypes?: string | undefined } = {}) {
|
22
|
+
const watcher = watchDirectory("", { depth: 1, cwd: srcDir, awaitWriteFinish: { stabilityThreshold: 100 } }).on("change", async path => {
|
23
|
+
const extension = getFileExtension(path)
|
24
|
+
|
25
|
+
if (supportedExtensions.includes(extension)) {
|
26
|
+
const name = getBaseName(path, extension)
|
27
|
+
const fileName = getBaseName(path)
|
28
|
+
|
29
|
+
if (path == fileName) {
|
30
|
+
if (!scripts.length || scripts.includes(name)) {
|
31
|
+
const sourceCode = await readFile(resolvePath(srcDir, path), { encoding: "utf-8" })
|
32
|
+
const skips = new Map<string, string[]>()
|
33
|
+
const promisesSkips: Promise<any>[] = []
|
34
|
+
|
35
|
+
for (const dir of await readDirectory(srcDir, { withFileTypes: true })) {
|
36
|
+
if (!dir.isDirectory())
|
37
|
+
continue
|
38
|
+
|
39
|
+
promisesSkips.push(readDirectory(resolvePath(srcDir, dir.name), { withFileTypes: true }).then(files => {
|
40
|
+
for (const file of files) {
|
41
|
+
if (!file.isFile())
|
42
|
+
continue
|
43
|
+
|
44
|
+
const fileExtension = getFileExtension(file.name)
|
45
|
+
|
46
|
+
if (!supportedExtensions.includes(fileExtension))
|
47
|
+
continue
|
48
|
+
|
49
|
+
const name = getBaseName(file.name, fileExtension)
|
50
|
+
const skip = skips.get(name)
|
51
|
+
|
52
|
+
if (skip)
|
53
|
+
skip.push(dir.name)
|
54
|
+
else
|
55
|
+
skips.set(name, [ dir.name ])
|
56
|
+
}
|
57
|
+
}))
|
58
|
+
}
|
59
|
+
|
60
|
+
await Promise.all(promisesSkips)
|
61
|
+
|
62
|
+
let error = null
|
63
|
+
|
64
|
+
const { script, srcLength } = await processScript(sourceCode).catch(reason => {
|
65
|
+
error = reason
|
66
|
+
|
67
|
+
return {
|
68
|
+
script: "",
|
69
|
+
srcLength: 0
|
70
|
+
}
|
71
|
+
})
|
72
|
+
|
73
|
+
const info: Info = {
|
74
|
+
file: path,
|
75
|
+
users: [],
|
76
|
+
minLength: 0,
|
77
|
+
error,
|
78
|
+
srcLength
|
79
|
+
}
|
80
|
+
|
81
|
+
const promises: Promise<any>[] = []
|
82
|
+
|
83
|
+
if (!error) {
|
84
|
+
if (script) {
|
85
|
+
const skip = skips.get(name) || []
|
86
|
+
|
87
|
+
info.minLength = countHackmudCharacters(script)
|
88
|
+
|
89
|
+
if (!users.length) {
|
90
|
+
users = (await readDirectory(hackmudDir, { withFileTypes: true }))
|
91
|
+
.filter(a => a.isFile() && getFileExtension(a.name) == ".key")
|
92
|
+
.map(a => getBaseName(a.name, ".key"))
|
93
|
+
}
|
94
|
+
|
95
|
+
for (const user of users) {
|
96
|
+
if (skip.includes(user))
|
97
|
+
continue
|
98
|
+
|
99
|
+
info.users.push(user)
|
100
|
+
promises.push(writeFilePersistent(resolvePath(hackmudDir, user, "scripts", `${name}.js`), script))
|
101
|
+
}
|
102
|
+
} else
|
103
|
+
info.error = new Error("processed script was empty")
|
104
|
+
}
|
105
|
+
|
106
|
+
if (onPush) {
|
107
|
+
await Promise.all(promises)
|
108
|
+
onPush(info)
|
109
|
+
}
|
110
|
+
}
|
111
|
+
} else {
|
112
|
+
const user = getBaseName(resolvePath(path, ".."))
|
113
|
+
|
114
|
+
if ((!users.length || users.includes(user)) && (!scripts.length || scripts.includes(name))) {
|
115
|
+
const sourceCode = await readFile(resolvePath(srcDir, path), { encoding: "utf-8" })
|
116
|
+
let error = null
|
117
|
+
|
118
|
+
const { script, srcLength } = await processScript(sourceCode).catch(reason => {
|
119
|
+
error = reason
|
120
|
+
|
121
|
+
return {
|
122
|
+
script: "",
|
123
|
+
srcLength: 0
|
124
|
+
}
|
125
|
+
})
|
126
|
+
|
127
|
+
const info: Info = {
|
128
|
+
file: path,
|
129
|
+
users: [ user ],
|
130
|
+
minLength: 0,
|
131
|
+
error,
|
132
|
+
srcLength
|
133
|
+
}
|
134
|
+
|
135
|
+
if (!error) {
|
136
|
+
if (script) {
|
137
|
+
info.minLength = countHackmudCharacters(script)
|
138
|
+
await writeFilePersistent(resolvePath(hackmudDir, user, "scripts", `${name}.js`), script)
|
139
|
+
} else
|
140
|
+
info.error = new Error("processed script was empty")
|
141
|
+
}
|
142
|
+
|
143
|
+
onPush?.(info)
|
144
|
+
}
|
145
|
+
}
|
146
|
+
}
|
147
|
+
})
|
148
|
+
|
149
|
+
if (genTypes) {
|
150
|
+
generateTypings(srcDir, resolvePath(srcDir, genTypes), hackmudDir)
|
151
|
+
watcher.on("add", () => generateTypings(srcDir, resolvePath(srcDir, genTypes), hackmudDir))
|
152
|
+
watcher.on("unlink", () => generateTypings(srcDir, resolvePath(srcDir, genTypes), hackmudDir))
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
156
|
+
export default watch
|
package/tsconfig.json
ADDED
package/assert-1b7dada8.js
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
class CustomError extends Error{constructor(...r){super(...r),function _defineProperty(r,e,s){return e in r?Object.defineProperty(r,e,{value:s,enumerable:!0,configurable:!0,writable:!0}):r[e]=s,r}(this,"name",this.constructor.name)}}class AssertError extends CustomError{}function assert(r,e="assertion failed"){if(!r)throw new AssertError(e)}function ensure(r,e="ensure failed"){return assert(r,e),r}export{assert as a,ensure as e};
|
package/bin/hsm.d.ts
DELETED
package/bin/hsm.js
DELETED
@@ -1,2 +0,0 @@
|
|
1
|
-
#!/usr/bin/env node
|
2
|
-
import o from"fs";import{resolve as e,basename as s,extname as t,dirname as r,relative as n}from"path";import{D as i,s as a,w as l,t as c,a as p,push as g}from"../push.js";import{g as b}from"../processScript/minify.js";import m from"chalk";import{homedir as u}from"os";import{processScript as f}from"../processScript/index.js";import{generateTypings as d}from"../generateTypings.js";import{syncMacros as h}from"../syncMacros.js";import{pull as k}from"../pull.js";import"chokidar";import"@babel/generator";import"@babel/parser";import"@babel/traverse";import"@babel/types";import"../assert-1b7dada8.js";import"../spliceString-2c6f214f.js";import"acorn";import"terser";import"perf_hooks";import"../processScript/compile.js";import"@babel/core";import"@babel/plugin-proposal-class-properties";import"@babel/plugin-proposal-class-static-block";import"@babel/plugin-proposal-decorators";import"@babel/plugin-proposal-do-expressions";import"@babel/plugin-proposal-function-bind";import"@babel/plugin-proposal-function-sent";import"@babel/plugin-proposal-json-strings";import"@babel/plugin-proposal-logical-assignment-operators";import"@babel/plugin-proposal-nullish-coalescing-operator";import"@babel/plugin-proposal-numeric-separator";import"@babel/plugin-proposal-object-rest-spread";import"@babel/plugin-proposal-optional-catch-binding";import"@babel/plugin-proposal-optional-chaining";import"@babel/plugin-proposal-partial-application";import"@babel/plugin-proposal-pipeline-operator";import"@babel/plugin-proposal-private-property-in-object";import"@babel/plugin-proposal-record-and-tuple";import"@babel/plugin-proposal-throw-expressions";import"@babel/plugin-transform-exponentiation-operator";import"@babel/plugin-transform-typescript";import"../processScript/postProcess.js";import"../processScript/preProcess.js";const{readFile:w,rmdir:y,writeFile:$,mkdir:j}=o.promises,v=e(u(),".config"),P=e(v,"hsm.json"),S=new Map,C=[];let x;const N=m.rgb(255,244,4),O=m.rgb(243,249,152),L=m.rgb(179,255,155),T=m.rgb(255,150,224),E=m.rgb(30,255,0),I=m.rgb(202,202,202),M=new i((o=>{let e=0;for(const s of o)e+=(e>>1)+e+"xi1_8ratvsw9hlbgm02y5zpdcn7uekof463qj".indexOf(s)+1;return[N,O,L,T,E,I][e%6](o)}));for(const o of process.argv.slice(2))if("-"==o[0]){const[e,s]=o.split("=");let t=s;if(t)if("true"==t)t=!0;else if("false"==t)t=!1;else{const o=Number(t);isFinite(o)&&(t=o)}else t=!0;if("-"==o[1])S.set(e.slice(2),t);else for(const o of e.slice(1))S.set(o,t)}else C.push(o);function help(){switch(C[0]){case"config":switch(C[1]){case"get":console.log("hsm config get <key>");break;case"set":console.log("hsm config set <key> <value>");break;case"delete":console.log("hsm config delete <key>");break;default:console.log("hsm config <get, delete, set>")}break;case"push":console.log('hsm push [<dir> [..."<script user>.<script name>"]]');break;case"watch":console.log("hsm watch [dir]");break;case"pull":console.log("hsm pull <script user>.<script name>");break;case"minify":case"golf":console.log(`${s(process.argv[1])} ${C[0]} <target> [output]`);break;default:console.log("hsm <push, watch, pull, config, golf>")}}async function version(){console.log("0.13.0-a60a7a2")}async function getConfig(){return x||(x=await w(P,{encoding:"utf-8"}).then((o=>{let e;try{e=JSON.parse(o)}catch{return console.log("config file was corrupted, resetting"),{}}return e&&"object"==typeof e?e:(console.log("config file was corrupted, resetting"),{})}),(()=>(console.log(`creating config file at ${P}`),{}))))}function exploreObject(o,e,s=!1){for(const r of e){var t;o=s?"object"==typeof o[r]?o[r]:o[r]={}:null===(t=o)||void 0===t?void 0:t[r]}return o}function updateConfig(){if(x){const o=JSON.stringify(x);$(P,o).catch((async e=>{switch(e.code){case"EISDIR":await y(P);break;case"ENOENT":await j(v);break;default:throw e}$(P,o)}))}}function onPushLogger({file:o,users:r,srcLength:n,minLength:i,error:a}){r.length&&(a?console.log(`error "${m.bold(a.message)}" in ${m.bold(o)}`):console.log(`pushed ${m.bold(o)} to ${r.map((o=>m.bold(M.get(o)))).join(", ")} | ${m.bold(String(i))} chars from ${m.bold(String(n))} | saved ${m.bold(String(n-i))} (${m.bold(`${Math.round(100*(1-i/n))}%`)}) | ${m.bold(`${e(x.hackmudPath,r[0],"scripts",s(o,t(o)))}.js`)}`))}(async()=>{if(S.get("version")||S.get("v"))version();else if(S.get("help")||S.get("h"))help();else{switch(C[0]){case"push":{const o=await getConfig();if(!o.hackmudPath){console.log("you need to set hackmudPath in config before you can use this command");break}const e=C[1]||".",s=o.hackmudPath,t=C.slice(2);t.length||t.push("*.*");(await g(e,s,{scripts:t,onPush:onPushLogger,minify:!S.get("skip-minify")})).length||console.warn("couldn't find any scripts to push"),updateConfig()}break;case"dev":case"watch":{var o,i,u;const e=await getConfig();if(!e.hackmudPath){console.log("you need to set hackmudPath in config before you can use this command");break}const s=C[1]||".",t=e.hackmudPath,r=(null===(o=S.get("users"))||void 0===o?void 0:o.toString().split(","))||[],n=(null===(i=S.get("scripts"))||void 0===i?void 0:i.toString().split(","))||[],a=null===(u=S.get("gen-types"))||void 0===u?void 0:u.toString();p(s,t,r,n,onPushLogger,{genTypes:a})}break;case"pull":{const o=await getConfig();if(!o.hackmudPath){console.log("you need to set hackmudPath in config before you can use this command");break}const e=C[1];if(!e){help();break}const s=C[2]||".",t=o.hackmudPath;try{await k(s,t,e)}catch(o){console.log("something went wrong, did you forget to #down the script?")}}break;case"sync-macros":{const{hackmudPath:o}=await getConfig();if(!o){console.log("you need to set hackmudPath in config before you can use this command");break}const{macrosSynced:e,usersSynced:s}=await h(o);console.log(`synced ${e} macros to ${s} users`)}break;case"test":{const o=e(C[1]||".");let s=0;console.log(`testing scripts in ${m.bold(o)}\n`);for(const{file:e,line:t,message:r}of await c(o))console.log(`error "${m.bold(r)}" in ${m.bold(e)} on line ${m.bold(String(t))}`),s++;if(!s){console.log("no errors found");break}if(s){process.exitCode=1,console.log(`\nencountered ${m.bold(String(s))} errors`);break}console.log("no errors found")}break;case"gen-types":{const o=e(C[1]||".");let s;s=C[2]?e(C[2]):e(o,"../player.d.ts"),d(o,s,(await getConfig()).hackmudPath)}break;case"config":switch(C[1]){case"get":console.log(exploreObject(await getConfig(),C[2].split(".")));break;case"delete":{var y;const o=C[2].split(".");if(!o.length){help();break}const e=await getConfig();null===(y=exploreObject(e,o))||void 0===y||delete y[C[3]],console.log(e)}break;case"set":{const o=C[2].split(".");if(!o.length){help();break}const s=await getConfig();let t=s;for(let e of o.slice(0,-1))t="object"==typeof t[e]?t[e]:t[e]={};t[o.slice(-1)[0]]=C[3],s.hackmudPath&&(s.hackmudPath=e(s.hackmudPath)),console.log(s)}break;default:C[1]&&console.log("unknown command"),help()}break;case"help":case"h":help();break;case"version":case"v":version();break;case"golf":case"minify":{if(!C[1]){console.log(`Target required\nUsage: ${s(process.argv[1])} ${C[0]} <target> [output]`);break}const o=t(C[1]);if(!a.includes(o)){console.log(`Unsupported file extension "${m.bold(o)}"\nSupported extensions are "${a.map((o=>m.bold(o))).join('", "')}"`);break}await w(C[1],{encoding:"utf-8"}).then((async t=>{const i=s(C[1],o),a=i.endsWith(".src"),c=a?i.slice(0,-4):i;let p="UNKNOWN";"scripts"==s(e(C[1],".."))&&"hackmud"==s(e(C[1],"../../.."))&&(p=s(e(C[1],"../..")));const{script:g,srcLength:u,warnings:d,timeTook:h}=await f(t,{minify:!S.get("skip-minify"),scriptUser:p,scriptName:c});for(const{message:o,line:e}of d)console.log(`warning "${m.bold(o)}" on line ${m.bold(String(e))}`);let k;k=C[2]?C[2]:e(r(C[1]),a?`${c}.js`:".js"==o?`${i}.min.js`:`${i}.js`);const w=b(g);await l(k,g).catch((async t=>{if(!C[2]||"EISDIR"!=t.code)throw t;k=e(k,`${s(C[1],o)}.js`),await l(k,g)})).then((()=>console.log(`wrote ${m.bold(w)} chars to ${m.bold(n(".",k))} | saved ${m.bold(u-w)} chars | took ${Math.round(100*h)/100}ms`)),(o=>console.log(o.message)))}),(o=>console.log(o.message)))}break;default:C[0]&&console.log("unknown command"),help()}updateConfig()}})();
|
package/generateTypings.d.ts
DELETED
package/generateTypings.js
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
import{promises as t}from"fs";import{extname as e,basename as n,resolve as s}from"path";const{readdir:r,writeFile:o}=t;async function generateTypings(t,a,i){const f=new Set;if(i)for(const t of await r(i,{withFileTypes:!0}))t.isFile()&&".key"==e(t.name)&&f.add(n(t.name,".key"));const c=[],l=[],p={},m={};for(const o of await r(t,{withFileTypes:!0}))if(o.isFile())".ts"==e(o.name)?c.push(n(o.name,".ts")):".js"==e(o.name)&&l.push(n(o.name,".js"));else if(o.isDirectory()){const a=p[o.name]=[],i=m[o.name]=[];f.add(o.name);for(const f of await r(s(t,o.name),{withFileTypes:!0}))f.isFile()&&(".ts"==e(f.name)?a.push(n(f.name,".ts")):".js"==e(f.name)&&i.push(n(f.name,".js")))}let y="";for(const t of c)y+=`import { script as $${t}$ } from "./src/${t}"\n`;y+="\n";for(const t in p){const e=p[t];for(const n of e)y+=`import { script as $${t}$${n}$ } from "./src/${t}/${n}"\n`}y+="\ntype ArrayRemoveFirst<A> = A extends [ infer FirstItem, ...infer Rest ] ? Rest : never\n\ntype Subscript<T extends (...args: any) => any> =\n\t(...args: ArrayRemoveFirst<Parameters<T>>) => ReturnType<T> | ScriptFailure\n\ntype WildFullsec = Record<string, () => ScriptFailure> & {\n";for(const t of c)y+=`\t${t}: Subscript<typeof $${t}$>\n`;for(const t of l)y+=`\t${t}: (...args: any) => any\n`;y+="}\n\ndeclare global {\n\tinterface PlayerFullsec {";let $=!0;for(const t of f){const e=p[t],n=m[t];if(e&&e.length||n&&n.length){$=!0,y+=`\n\t\t${t}: WildFullsec & {\n`;for(const n of e)y+=`\t\t\t${n}: Subscript<typeof $${t}$${n}$>\n`;for(const t of n)y+=`\t\t\t${t}: (...args: any) => any\n`;y+="\t\t}"}else $&&(y+="\n",$=!1),y+=`\t\t${t}: WildFullsec`;y+="\n"}y+="\t}\n}\n",await o(a,y)}export{generateTypings as default,generateTypings};
|
package/index.d.ts
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
export interface Info {
|
2
|
-
file: string;
|
3
|
-
users: string[];
|
4
|
-
srcLength: number;
|
5
|
-
minLength: number;
|
6
|
-
error: Error | null;
|
7
|
-
}
|
8
|
-
export declare const supportedExtensions: string[];
|
9
|
-
export { generateTypings } from "./generateTypings";
|
10
|
-
export { processScript } from "./processScript";
|
11
|
-
export { pull } from "./pull";
|
12
|
-
export { push } from "./push";
|
13
|
-
export { syncMacros } from "./syncMacros";
|
14
|
-
export { test } from "./test";
|
15
|
-
export { watch } from "./watch";
|