hackmud-script-manager 0.13.0-c14e977 → 0.13.0-c461329
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/.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 +17 -23
- 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-c14e977")}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";
|