hackmud-script-manager 0.11.0 → 0.12.0-2ac9138
Sign up to get free protection for your applications and to get access to all the features.
- package/bin/hsm.js +55 -53
- package/index.js +10 -857
- package/package.json +11 -7
- package/shared.js +901 -0
- package/lib.js +0 -71
package/bin/hsm.js
CHANGED
@@ -1,28 +1,30 @@
|
|
1
1
|
#!/usr/bin/env node
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
const
|
2
|
+
import { resolve, basename, extname, dirname, relative } from 'path';
|
3
|
+
import { homedir } from 'os';
|
4
|
+
import chalk from 'chalk';
|
5
|
+
import fs from 'fs';
|
6
|
+
import { D as DynamicMap, s as supportedExtensions, p as processScript, h as hackmudLength, w as writeFilePersist, g as generateTypings, t as test, a as syncMacros, b as pull, c as watch, d as push } from '../shared.js';
|
7
|
+
import 'acorn';
|
8
|
+
import 'chokidar';
|
9
|
+
import 'escodegen';
|
10
|
+
import 'esprima';
|
11
|
+
import 'esquery';
|
12
|
+
import 'terser';
|
13
|
+
import 'typescript';
|
14
|
+
|
15
|
+
const { readFile: readFile, rmdir: removeDirectory, writeFile: writeFile, mkdir: makeDirectory } = fs.promises;
|
16
|
+
const configDirPath = resolve(homedir(), ".config");
|
17
|
+
const configFilePath = resolve(configDirPath, "hsm.json");
|
16
18
|
const options = new Map();
|
17
19
|
const commands = [];
|
18
20
|
let config;
|
19
|
-
const colourJ =
|
20
|
-
const colourK =
|
21
|
-
const colourM =
|
22
|
-
const colourW =
|
23
|
-
const colourL =
|
24
|
-
const colourB =
|
25
|
-
const userColours = new
|
21
|
+
const colourJ = chalk.rgb(0xFF, 0xF4, 0x04);
|
22
|
+
const colourK = chalk.rgb(0xF3, 0xF9, 0x98);
|
23
|
+
const colourM = chalk.rgb(0xB3, 0xFF, 0x9B);
|
24
|
+
const colourW = chalk.rgb(0xFF, 0x96, 0xE0);
|
25
|
+
const colourL = chalk.rgb(0x1E, 0xFF, 0x00);
|
26
|
+
const colourB = chalk.rgb(0xCA, 0xCA, 0xCA);
|
27
|
+
const userColours = new DynamicMap(user => {
|
26
28
|
let hash = 0;
|
27
29
|
for (const char of user)
|
28
30
|
hash += (hash >> 1) + hash + "xi1_8ratvsw9hlbgm02y5zpdcn7uekof463qj".indexOf(char) + 1;
|
@@ -80,7 +82,7 @@ for (const arg of process.argv.slice(2)) {
|
|
80
82
|
if (commands[2]) {
|
81
83
|
const match = commands[2].match(/^([a-z_][a-z_0-9]{0,24})\.([a-z_][a-z_0-9]{0,24})$/);
|
82
84
|
if (!match) {
|
83
|
-
console.log(`"${
|
85
|
+
console.log(`"${chalk.bold(commands[2])}" is not a valid script name`);
|
84
86
|
break;
|
85
87
|
}
|
86
88
|
users = [match[1]];
|
@@ -90,7 +92,7 @@ for (const arg of process.argv.slice(2)) {
|
|
90
92
|
users = ((_a = options.get("users")) === null || _a === void 0 ? void 0 : _a.toString().split(",")) || [];
|
91
93
|
scripts = ((_b = options.get("scripts")) === null || _b === void 0 ? void 0 : _b.toString().split(",")) || [];
|
92
94
|
}
|
93
|
-
await
|
95
|
+
await push(srcPath, hackmudPath, users, scripts, onPushLogger);
|
94
96
|
updateConfig();
|
95
97
|
}
|
96
98
|
break;
|
@@ -107,7 +109,7 @@ for (const arg of process.argv.slice(2)) {
|
|
107
109
|
const users = ((_c = options.get("users")) === null || _c === void 0 ? void 0 : _c.toString().split(",")) || [];
|
108
110
|
const scripts = ((_d = options.get("scripts")) === null || _d === void 0 ? void 0 : _d.toString().split(",")) || [];
|
109
111
|
const genTypes = (_e = options.get("gen-types")) === null || _e === void 0 ? void 0 : _e.toString();
|
110
|
-
|
112
|
+
watch(srcPath, hackmudPath, users, scripts, onPushLogger, { genTypes });
|
111
113
|
}
|
112
114
|
break;
|
113
115
|
case "pull":
|
@@ -125,7 +127,7 @@ for (const arg of process.argv.slice(2)) {
|
|
125
127
|
const srcPath = commands[2] || ".";
|
126
128
|
const hackmudPath = config.hackmudPath;
|
127
129
|
try {
|
128
|
-
await
|
130
|
+
await pull(srcPath, hackmudPath, script);
|
129
131
|
}
|
130
132
|
catch (error) {
|
131
133
|
console.log("something went wrong, did you forget to #down the script?");
|
@@ -139,17 +141,17 @@ for (const arg of process.argv.slice(2)) {
|
|
139
141
|
console.log("you need to set hackmudPath in config before you can use this command");
|
140
142
|
break;
|
141
143
|
}
|
142
|
-
const { macrosSynced, usersSynced } = await
|
144
|
+
const { macrosSynced, usersSynced } = await syncMacros(hackmudPath);
|
143
145
|
console.log(`synced ${macrosSynced} macros to ${usersSynced} users`);
|
144
146
|
}
|
145
147
|
break;
|
146
148
|
case "test":
|
147
149
|
{
|
148
|
-
const srcPath =
|
150
|
+
const srcPath = resolve(commands[1] || ".");
|
149
151
|
let errors = 0;
|
150
|
-
console.log(`testing scripts in ${
|
151
|
-
for (const { file, line, message } of await
|
152
|
-
console.log(`error "${
|
152
|
+
console.log(`testing scripts in ${chalk.bold(srcPath)}\n`);
|
153
|
+
for (const { file, line, message } of await test(srcPath)) {
|
154
|
+
console.log(`error "${chalk.bold(message)}" in ${chalk.bold(file)} on line ${chalk.bold(String(line))}`);
|
153
155
|
errors++;
|
154
156
|
}
|
155
157
|
if (!errors) {
|
@@ -158,7 +160,7 @@ for (const arg of process.argv.slice(2)) {
|
|
158
160
|
}
|
159
161
|
if (errors) {
|
160
162
|
process.exitCode = 1;
|
161
|
-
console.log(`\nencountered ${
|
163
|
+
console.log(`\nencountered ${chalk.bold(String(errors))} errors`);
|
162
164
|
break;
|
163
165
|
}
|
164
166
|
console.log("no errors found");
|
@@ -166,13 +168,13 @@ for (const arg of process.argv.slice(2)) {
|
|
166
168
|
break;
|
167
169
|
case "gen-types":
|
168
170
|
{
|
169
|
-
const srcPath =
|
171
|
+
const srcPath = resolve(commands[1] || ".");
|
170
172
|
let targetPath;
|
171
173
|
if (commands[2])
|
172
|
-
targetPath =
|
174
|
+
targetPath = resolve(commands[2]);
|
173
175
|
else
|
174
|
-
targetPath =
|
175
|
-
|
176
|
+
targetPath = resolve(srcPath, "../player.d.ts");
|
177
|
+
generateTypings(srcPath, targetPath, (await getConfig()).hackmudPath);
|
176
178
|
}
|
177
179
|
break;
|
178
180
|
case "config":
|
@@ -208,7 +210,7 @@ for (const arg of process.argv.slice(2)) {
|
|
208
210
|
object = typeof object[key] == "object" ? object[key] : object[key] = {};
|
209
211
|
object[keys.slice(-1)[0]] = value;
|
210
212
|
if (config.hackmudPath)
|
211
|
-
config.hackmudPath =
|
213
|
+
config.hackmudPath = resolve(config.hackmudPath);
|
212
214
|
console.log(config);
|
213
215
|
}
|
214
216
|
break;
|
@@ -235,38 +237,38 @@ for (const arg of process.argv.slice(2)) {
|
|
235
237
|
case "minify":
|
236
238
|
{
|
237
239
|
if (!commands[1]) {
|
238
|
-
console.log(`Target required\nUsage: ${
|
240
|
+
console.log(`Target required\nUsage: ${basename(process.argv[1])} ${commands[0]} <target> [output]`);
|
239
241
|
break;
|
240
242
|
}
|
241
|
-
const fileExtension =
|
242
|
-
if (!
|
243
|
-
console.log(`Unsupported file extension "${
|
243
|
+
const fileExtension = extname(commands[1]);
|
244
|
+
if (!supportedExtensions.includes(fileExtension)) {
|
245
|
+
console.log(`Unsupported file extension "${chalk.bold(fileExtension)}"\nSupported extensions are "${supportedExtensions.map(extension => chalk.bold(extension)).join('", "')}"`);
|
244
246
|
break;
|
245
247
|
}
|
246
248
|
await readFile(commands[1], { encoding: "utf-8" }).then(async (source) => {
|
247
|
-
const { script, srcLength, warnings } = await
|
249
|
+
const { script, srcLength, warnings } = await processScript(source);
|
248
250
|
for (const { message, line } of warnings)
|
249
|
-
console.log(`warning "${
|
251
|
+
console.log(`warning "${chalk.bold(message)}" on line ${chalk.bold(String(line))}`);
|
250
252
|
let outputPath;
|
251
253
|
if (commands[2])
|
252
254
|
outputPath = commands[2];
|
253
255
|
else {
|
254
|
-
const fileBaseName =
|
255
|
-
outputPath =
|
256
|
+
const fileBaseName = basename(commands[1], fileExtension);
|
257
|
+
outputPath = resolve(dirname(commands[1]), fileBaseName.endsWith(".src")
|
256
258
|
? `${fileBaseName.slice(0, -4)}.js` :
|
257
259
|
fileExtension == ".js"
|
258
260
|
? `${fileBaseName}.min.js`
|
259
261
|
: `${fileBaseName}.js`);
|
260
262
|
}
|
261
|
-
const scriptLength =
|
262
|
-
await
|
263
|
+
const scriptLength = hackmudLength(script);
|
264
|
+
await writeFilePersist(outputPath, script)
|
263
265
|
.catch(async (error) => {
|
264
266
|
if (!commands[2] || error.code != "EISDIR")
|
265
267
|
throw error;
|
266
|
-
outputPath =
|
267
|
-
await
|
268
|
+
outputPath = resolve(outputPath, `${basename(commands[1], fileExtension)}.js`);
|
269
|
+
await writeFilePersist(outputPath, script);
|
268
270
|
})
|
269
|
-
.then(() => console.log(`wrote ${
|
271
|
+
.then(() => console.log(`wrote ${chalk.bold(scriptLength)} chars to ${chalk.bold(relative(".", outputPath))} | saved ${chalk.bold(srcLength - scriptLength)} chars`), (error) => console.log(error.message));
|
270
272
|
}, (error) => console.log(error.message));
|
271
273
|
}
|
272
274
|
break;
|
@@ -322,7 +324,7 @@ function help() {
|
|
322
324
|
case "minify":
|
323
325
|
case "golf":
|
324
326
|
{
|
325
|
-
console.log(`${
|
327
|
+
console.log(`${basename(process.argv[1])} ${commands[0]} <target> [output]`);
|
326
328
|
}
|
327
329
|
break;
|
328
330
|
default: {
|
@@ -331,7 +333,7 @@ function help() {
|
|
331
333
|
}
|
332
334
|
}
|
333
335
|
async function version() {
|
334
|
-
console.log(JSON.parse(await readFile(
|
336
|
+
console.log(JSON.parse(await readFile(resolve(__dirname, "../package.json"), { encoding: "utf-8" })).version || "unknown");
|
335
337
|
}
|
336
338
|
async function getConfig() {
|
337
339
|
if (config)
|
@@ -393,8 +395,8 @@ function onPushLogger({ file, users, srcLength, minLength, error }) {
|
|
393
395
|
if (!users.length)
|
394
396
|
return;
|
395
397
|
if (error) {
|
396
|
-
console.log(`error "${
|
398
|
+
console.log(`error "${chalk.bold(error.message)}" in ${chalk.bold(file)}`);
|
397
399
|
return;
|
398
400
|
}
|
399
|
-
console.log(`pushed ${
|
401
|
+
console.log(`pushed ${chalk.bold(file)} to ${users.map(user => chalk.bold(userColours.get(user))).join(", ")} | ${chalk.bold(String(minLength))} chars from ${chalk.bold(String(srcLength))} | saved ${chalk.bold(String(srcLength - minLength))} (${chalk.bold(`${Math.round((1 - (minLength / srcLength)) * 100)}%`)}) | ${chalk.bold(`${resolve(config.hackmudPath, users[0], "scripts", basename(file, extname(file)))}.js`)}`);
|
400
402
|
}
|
package/index.js
CHANGED
@@ -1,857 +1,10 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
const esquery_1 = require("esquery");
|
12
|
-
const fs_1 = __importDefault(require("fs"));
|
13
|
-
const path_1 = require("path");
|
14
|
-
const terser_1 = require("terser");
|
15
|
-
const typescript_1 = __importDefault(require("typescript"));
|
16
|
-
const lib_1 = require("./lib");
|
17
|
-
const { readFile: readFile, readdir: readDirectory, stat: getFileStatus, writeFile: writeFile } = fs_1.default.promises;
|
18
|
-
exports.supportedExtensions = [".js", ".ts"];
|
19
|
-
// TODO `clean()` function that delete all scripts in hackmud directory #70
|
20
|
-
// TODO optional argument (defaults to false) for `clean()` that makes it only remove scripts without a source file #70
|
21
|
-
/**
|
22
|
-
* Push a specific or all scripts to a specific or all users.
|
23
|
-
* In source directory, scripts in folders will override scripts with same name for user with folder name.
|
24
|
-
*
|
25
|
-
* e.g. foo/bar.js overrides other bar.js script just for user foo.
|
26
|
-
*
|
27
|
-
* @param srcDir path to folder containing source files
|
28
|
-
* @param hackmudDir path to hackmud directory
|
29
|
-
* @param users users to push to (pushes to all if empty)
|
30
|
-
* @param scripts scripts to push from (pushes from all if empty)
|
31
|
-
* @param onPush function that's called when a script has been pushed
|
32
|
-
*/
|
33
|
-
function push(srcDir, hackmudDir, users, scripts, onPush) {
|
34
|
-
return new Promise(async (resolve) => {
|
35
|
-
const infoAll = [];
|
36
|
-
const files = await readDirectory(srcDir, { withFileTypes: true });
|
37
|
-
const skips = new Map();
|
38
|
-
const promises = [];
|
39
|
-
for (const dir of files) {
|
40
|
-
const user = dir.name;
|
41
|
-
if (dir.isDirectory() && (!users.length || users.includes(user))) {
|
42
|
-
promises.push(readDirectory((0, path_1.resolve)(srcDir, user), { withFileTypes: true }).then(files => {
|
43
|
-
for (const file of files) {
|
44
|
-
const extension = (0, path_1.extname)(file.name);
|
45
|
-
const name = (0, path_1.basename)(file.name, extension);
|
46
|
-
if (exports.supportedExtensions.includes(extension) && file.isFile() && (!scripts.length || scripts.includes(name))) {
|
47
|
-
let skip = skips.get(name);
|
48
|
-
if (skip)
|
49
|
-
skip.push(user);
|
50
|
-
else
|
51
|
-
skips.set(name, [user]);
|
52
|
-
readFile((0, path_1.resolve)(srcDir, user, file.name), { encoding: "utf-8" }).then(async (code) => {
|
53
|
-
let error = null;
|
54
|
-
const { srcLength, script: minCode } = await processScript(code).catch(reason => {
|
55
|
-
error = reason;
|
56
|
-
return {
|
57
|
-
srcLength: 0,
|
58
|
-
script: ""
|
59
|
-
};
|
60
|
-
});
|
61
|
-
const info = {
|
62
|
-
file: `${user}/${file.name}`,
|
63
|
-
users: [user],
|
64
|
-
minLength: 0,
|
65
|
-
error,
|
66
|
-
srcLength
|
67
|
-
};
|
68
|
-
infoAll.push(info);
|
69
|
-
if (!error) {
|
70
|
-
if (minCode) {
|
71
|
-
info.minLength = (0, lib_1.hackmudLength)(minCode);
|
72
|
-
await (0, lib_1.writeFilePersist)((0, path_1.resolve)(hackmudDir, user, "scripts", `${name}.js`), minCode);
|
73
|
-
}
|
74
|
-
else
|
75
|
-
info.error = new Error("processed script was empty");
|
76
|
-
}
|
77
|
-
onPush === null || onPush === void 0 ? void 0 : onPush(info);
|
78
|
-
});
|
79
|
-
}
|
80
|
-
}
|
81
|
-
}));
|
82
|
-
}
|
83
|
-
}
|
84
|
-
if (!users.length) {
|
85
|
-
users = (await readDirectory(hackmudDir, { withFileTypes: true }))
|
86
|
-
.filter(a => a.isFile() && (0, path_1.extname)(a.name) == ".key")
|
87
|
-
.map(a => (0, path_1.basename)(a.name, ".key"));
|
88
|
-
}
|
89
|
-
Promise.all(promises).then(() => {
|
90
|
-
const promises = [];
|
91
|
-
for (const file of files) {
|
92
|
-
if (file.isFile()) {
|
93
|
-
const extension = (0, path_1.extname)(file.name);
|
94
|
-
if (exports.supportedExtensions.includes(extension)) {
|
95
|
-
const name = (0, path_1.basename)(file.name, extension);
|
96
|
-
if (!scripts.length || scripts.includes(name)) {
|
97
|
-
promises.push(readFile((0, path_1.resolve)(srcDir, file.name), { encoding: "utf-8" }).then(async (code) => {
|
98
|
-
let error = null;
|
99
|
-
const { script: minCode, srcLength } = await processScript(code).catch(reason => {
|
100
|
-
error = reason;
|
101
|
-
return {
|
102
|
-
script: "",
|
103
|
-
srcLength: 0
|
104
|
-
};
|
105
|
-
});
|
106
|
-
const info = {
|
107
|
-
file: file.name,
|
108
|
-
users: [],
|
109
|
-
minLength: 0,
|
110
|
-
error,
|
111
|
-
srcLength
|
112
|
-
};
|
113
|
-
infoAll.push(info);
|
114
|
-
if (!error) {
|
115
|
-
if (minCode) {
|
116
|
-
info.minLength = (0, lib_1.hackmudLength)(minCode);
|
117
|
-
const skip = skips.get(name) || [];
|
118
|
-
const promises = [];
|
119
|
-
for (const user of users) {
|
120
|
-
if (!skip.includes(user)) {
|
121
|
-
info.users.push(user);
|
122
|
-
promises.push((0, lib_1.writeFilePersist)((0, path_1.resolve)(hackmudDir, user, "scripts", `${name}.js`), minCode));
|
123
|
-
}
|
124
|
-
}
|
125
|
-
}
|
126
|
-
else
|
127
|
-
info.error = new Error("processed script was empty");
|
128
|
-
}
|
129
|
-
if (onPush)
|
130
|
-
Promise.all(promises).then(() => onPush(info));
|
131
|
-
}));
|
132
|
-
}
|
133
|
-
}
|
134
|
-
}
|
135
|
-
}
|
136
|
-
Promise.all(promises).then(() => resolve(infoAll));
|
137
|
-
});
|
138
|
-
});
|
139
|
-
}
|
140
|
-
exports.push = push;
|
141
|
-
/**
|
142
|
-
* Watches target file or folder for updates and builds and pushes updated file.
|
143
|
-
*
|
144
|
-
* @param srcDir path to folder containing source files
|
145
|
-
* @param hackmudDir path to hackmud directory
|
146
|
-
* @param users users to push to (pushes to all if empty)
|
147
|
-
* @param scripts scripts to push from (pushes from all if empty)
|
148
|
-
* @param onPush function that's called after each script has been built and written
|
149
|
-
*/
|
150
|
-
function watch(srcDir, hackmudDir, users, scripts, onPush, { genTypes } = {}) {
|
151
|
-
const watcher = (0, chokidar_1.watch)("", { depth: 1, cwd: srcDir, awaitWriteFinish: { stabilityThreshold: 100 } }).on("change", async (path) => {
|
152
|
-
const extension = (0, path_1.extname)(path);
|
153
|
-
if (exports.supportedExtensions.includes(extension)) {
|
154
|
-
const name = (0, path_1.basename)(path, extension);
|
155
|
-
const fileName = (0, path_1.basename)(path);
|
156
|
-
if (path == fileName) {
|
157
|
-
if (!scripts.length || scripts.includes(name)) {
|
158
|
-
const sourceCode = await readFile((0, path_1.resolve)(srcDir, path), { encoding: "utf-8" });
|
159
|
-
const skips = new Map();
|
160
|
-
const promisesSkips = [];
|
161
|
-
for (const dir of await readDirectory(srcDir, { withFileTypes: true })) {
|
162
|
-
if (!dir.isDirectory())
|
163
|
-
continue;
|
164
|
-
promisesSkips.push(readDirectory((0, path_1.resolve)(srcDir, dir.name), { withFileTypes: true }).then(files => {
|
165
|
-
for (const file of files) {
|
166
|
-
if (!file.isFile())
|
167
|
-
continue;
|
168
|
-
const fileExtension = (0, path_1.extname)(file.name);
|
169
|
-
if (!exports.supportedExtensions.includes(fileExtension))
|
170
|
-
continue;
|
171
|
-
const name = (0, path_1.basename)(file.name, fileExtension);
|
172
|
-
const skip = skips.get(name);
|
173
|
-
if (skip)
|
174
|
-
skip.push(dir.name);
|
175
|
-
else
|
176
|
-
skips.set(name, [dir.name]);
|
177
|
-
}
|
178
|
-
}));
|
179
|
-
}
|
180
|
-
await Promise.all(promisesSkips);
|
181
|
-
let error = null;
|
182
|
-
const { script, srcLength } = await processScript(sourceCode).catch(reason => {
|
183
|
-
error = reason;
|
184
|
-
return {
|
185
|
-
script: "",
|
186
|
-
srcLength: 0
|
187
|
-
};
|
188
|
-
});
|
189
|
-
const info = {
|
190
|
-
file: path,
|
191
|
-
users: [],
|
192
|
-
minLength: 0,
|
193
|
-
error,
|
194
|
-
srcLength
|
195
|
-
};
|
196
|
-
const promises = [];
|
197
|
-
if (!error) {
|
198
|
-
if (script) {
|
199
|
-
const skip = skips.get(name) || [];
|
200
|
-
info.minLength = (0, lib_1.hackmudLength)(script);
|
201
|
-
if (!users.length) {
|
202
|
-
users = (await readDirectory(hackmudDir, { withFileTypes: true }))
|
203
|
-
.filter(a => a.isFile() && (0, path_1.extname)(a.name) == ".key")
|
204
|
-
.map(a => (0, path_1.basename)(a.name, ".key"));
|
205
|
-
}
|
206
|
-
for (const user of users) {
|
207
|
-
if (skip.includes(user))
|
208
|
-
continue;
|
209
|
-
info.users.push(user);
|
210
|
-
promises.push((0, lib_1.writeFilePersist)((0, path_1.resolve)(hackmudDir, user, "scripts", `${name}.js`), script));
|
211
|
-
}
|
212
|
-
}
|
213
|
-
else
|
214
|
-
info.error = new Error("processed script was empty");
|
215
|
-
}
|
216
|
-
if (onPush) {
|
217
|
-
await Promise.all(promises);
|
218
|
-
onPush(info);
|
219
|
-
}
|
220
|
-
}
|
221
|
-
}
|
222
|
-
else {
|
223
|
-
const user = (0, path_1.basename)((0, path_1.resolve)(path, ".."));
|
224
|
-
if ((!users.length || users.includes(user)) && (!scripts.length || scripts.includes(name))) {
|
225
|
-
const sourceCode = await readFile((0, path_1.resolve)(srcDir, path), { encoding: "utf-8" });
|
226
|
-
let error = null;
|
227
|
-
const { script, srcLength } = await processScript(sourceCode).catch(reason => {
|
228
|
-
error = reason;
|
229
|
-
return {
|
230
|
-
script: "",
|
231
|
-
srcLength: 0
|
232
|
-
};
|
233
|
-
});
|
234
|
-
const info = {
|
235
|
-
file: path,
|
236
|
-
users: [user],
|
237
|
-
minLength: 0,
|
238
|
-
error,
|
239
|
-
srcLength
|
240
|
-
};
|
241
|
-
if (!error) {
|
242
|
-
if (script) {
|
243
|
-
info.minLength = (0, lib_1.hackmudLength)(script);
|
244
|
-
await (0, lib_1.writeFilePersist)((0, path_1.resolve)(hackmudDir, user, "scripts", `${name}.js`), script);
|
245
|
-
}
|
246
|
-
else
|
247
|
-
info.error = new Error("processed script was empty");
|
248
|
-
}
|
249
|
-
onPush === null || onPush === void 0 ? void 0 : onPush(info);
|
250
|
-
}
|
251
|
-
}
|
252
|
-
}
|
253
|
-
});
|
254
|
-
if (genTypes) {
|
255
|
-
generateTypings(srcDir, (0, path_1.resolve)(srcDir, genTypes), hackmudDir);
|
256
|
-
watcher.on("add", () => generateTypings(srcDir, (0, path_1.resolve)(srcDir, genTypes), hackmudDir));
|
257
|
-
watcher.on("unlink", () => generateTypings(srcDir, (0, path_1.resolve)(srcDir, genTypes), hackmudDir));
|
258
|
-
}
|
259
|
-
}
|
260
|
-
exports.watch = watch;
|
261
|
-
/**
|
262
|
-
* Copies script from hackmud to local source folder.
|
263
|
-
*
|
264
|
-
* @param sourceFolderPath path to folder containing source files
|
265
|
-
* @param hackmudPath path to hackmud directory
|
266
|
-
* @param script script to pull in `user.name` format
|
267
|
-
*/
|
268
|
-
async function pull(sourceFolderPath, hackmudPath, script) {
|
269
|
-
const [user, name] = script.split(".");
|
270
|
-
await (0, lib_1.copyFilePersist)((0, path_1.resolve)(hackmudPath, user, "scripts", `${name}.js`), (0, path_1.resolve)(sourceFolderPath, user, `${name}.js`));
|
271
|
-
}
|
272
|
-
exports.pull = pull;
|
273
|
-
async function syncMacros(hackmudPath) {
|
274
|
-
const files = await readDirectory(hackmudPath, { withFileTypes: true });
|
275
|
-
const macros = new Map();
|
276
|
-
const users = [];
|
277
|
-
for (const file of files) {
|
278
|
-
if (!file.isFile())
|
279
|
-
continue;
|
280
|
-
switch ((0, path_1.extname)(file.name)) {
|
281
|
-
case ".macros":
|
282
|
-
{
|
283
|
-
const lines = (await readFile((0, path_1.resolve)(hackmudPath, file.name), { encoding: "utf-8" })).split("\n");
|
284
|
-
const date = (await getFileStatus((0, path_1.resolve)(hackmudPath, file.name))).mtime;
|
285
|
-
for (let i = 0; i < lines.length / 2 - 1; i++) {
|
286
|
-
const macroName = lines[i * 2];
|
287
|
-
const curMacro = macros.get(macroName);
|
288
|
-
if (!curMacro || date > curMacro.date)
|
289
|
-
macros.set(macroName, { date, macro: lines[i * 2 + 1] });
|
290
|
-
}
|
291
|
-
}
|
292
|
-
break;
|
293
|
-
case ".key":
|
294
|
-
{
|
295
|
-
users.push((0, path_1.basename)(file.name, ".key"));
|
296
|
-
}
|
297
|
-
break;
|
298
|
-
}
|
299
|
-
}
|
300
|
-
let macroFile = "";
|
301
|
-
let macrosSynced = 0;
|
302
|
-
for (const [name, { macro }] of [...macros].sort(([a], [b]) => (a > b) - (a < b))) {
|
303
|
-
if (macro[0] != macro[0].toLowerCase())
|
304
|
-
continue;
|
305
|
-
macroFile += `${name}\n${macro}\n`;
|
306
|
-
macrosSynced++;
|
307
|
-
}
|
308
|
-
for (const user of users)
|
309
|
-
writeFile((0, path_1.resolve)(hackmudPath, user + ".macros"), macroFile);
|
310
|
-
return { macrosSynced, usersSynced: users.length };
|
311
|
-
}
|
312
|
-
exports.syncMacros = syncMacros;
|
313
|
-
async function test(srcPath) {
|
314
|
-
const promises = [];
|
315
|
-
const errors = [];
|
316
|
-
for (const dirent of await readDirectory(srcPath, { withFileTypes: true })) {
|
317
|
-
if (dirent.isDirectory()) {
|
318
|
-
promises.push(readDirectory((0, path_1.resolve)(srcPath, dirent.name), { withFileTypes: true }).then(files => {
|
319
|
-
const promises = [];
|
320
|
-
for (const file of files) {
|
321
|
-
if (!file.isFile() || !exports.supportedExtensions.includes((0, path_1.extname)(file.name)))
|
322
|
-
continue;
|
323
|
-
promises.push(readFile((0, path_1.resolve)(srcPath, dirent.name, file.name), { encoding: "utf-8" })
|
324
|
-
.then(processScript)
|
325
|
-
.then(({ warnings }) => errors.push(...warnings.map(({ message, line }) => ({
|
326
|
-
file: `${dirent.name}/${file.name}`,
|
327
|
-
message, line
|
328
|
-
})))));
|
329
|
-
}
|
330
|
-
return Promise.all(promises);
|
331
|
-
}));
|
332
|
-
}
|
333
|
-
else if (dirent.isFile() && exports.supportedExtensions.includes((0, path_1.extname)(dirent.name))) {
|
334
|
-
promises.push(readFile((0, path_1.resolve)(srcPath, dirent.name), { encoding: "utf-8" })
|
335
|
-
.then(processScript)
|
336
|
-
.then(({ warnings }) => errors.push(...warnings.map(({ message, line }) => ({
|
337
|
-
file: dirent.name,
|
338
|
-
message, line
|
339
|
-
})))));
|
340
|
-
}
|
341
|
-
}
|
342
|
-
await Promise.all(promises);
|
343
|
-
return errors;
|
344
|
-
}
|
345
|
-
exports.test = test;
|
346
|
-
async function generateTypings(srcDir, target, hackmudPath) {
|
347
|
-
const users = new Set();
|
348
|
-
if (hackmudPath) {
|
349
|
-
for (const dirent of await readDirectory(hackmudPath, { withFileTypes: true })) {
|
350
|
-
if (dirent.isFile() && (0, path_1.extname)(dirent.name) == ".key")
|
351
|
-
users.add((0, path_1.basename)(dirent.name, ".key"));
|
352
|
-
}
|
353
|
-
}
|
354
|
-
const wildScripts = [];
|
355
|
-
const wildAnyScripts = [];
|
356
|
-
const allScripts = {};
|
357
|
-
const allAnyScripts = {};
|
358
|
-
for (const dirent of await readDirectory(srcDir, { withFileTypes: true })) {
|
359
|
-
if (dirent.isFile()) {
|
360
|
-
if ((0, path_1.extname)(dirent.name) == ".ts")
|
361
|
-
wildScripts.push((0, path_1.basename)(dirent.name, ".ts"));
|
362
|
-
else if ((0, path_1.extname)(dirent.name) == ".js")
|
363
|
-
wildAnyScripts.push((0, path_1.basename)(dirent.name, ".js"));
|
364
|
-
}
|
365
|
-
else if (dirent.isDirectory()) {
|
366
|
-
const scripts = allScripts[dirent.name] = [];
|
367
|
-
const anyScripts = allAnyScripts[dirent.name] = [];
|
368
|
-
users.add(dirent.name);
|
369
|
-
for (const file of await readDirectory((0, path_1.resolve)(srcDir, dirent.name), { withFileTypes: true })) {
|
370
|
-
if (file.isFile()) {
|
371
|
-
if ((0, path_1.extname)(file.name) == ".ts")
|
372
|
-
scripts.push((0, path_1.basename)(file.name, ".ts"));
|
373
|
-
else if ((0, path_1.extname)(file.name) == ".js")
|
374
|
-
anyScripts.push((0, path_1.basename)(file.name, ".js"));
|
375
|
-
}
|
376
|
-
}
|
377
|
-
}
|
378
|
-
}
|
379
|
-
let o = "";
|
380
|
-
for (const script of wildScripts)
|
381
|
-
o += `import { script as $${script}$ } from "./src/${script}"\n`;
|
382
|
-
o += "\n";
|
383
|
-
for (const user in allScripts) {
|
384
|
-
const scripts = allScripts[user];
|
385
|
-
for (const script of scripts)
|
386
|
-
o += `import { script as $${user}$${script}$ } from "./src/${user}/${script}"\n`;
|
387
|
-
}
|
388
|
-
// TODO detect security level and generate apropriate code
|
389
|
-
// TODO accurate function signatures
|
390
|
-
// currently I lose the generic-ness of my functions when I wrap them
|
391
|
-
// just regexing isn't enough and it looks like I'm going to need to parse the files in TypeScript to extract the signature
|
392
|
-
o += `
|
393
|
-
type ArrayRemoveFirst<A> = A extends [ infer FirstItem, ...infer Rest ] ? Rest : never
|
394
|
-
|
395
|
-
type Subscript<T extends (...args: any) => any> =
|
396
|
-
(...args: ArrayRemoveFirst<Parameters<T>>) => ReturnType<T> | ScriptFailure
|
397
|
-
|
398
|
-
type WildFullsec = Record<string, () => ScriptFailure> & {
|
399
|
-
`;
|
400
|
-
for (const script of wildScripts)
|
401
|
-
o += `\t${script}: Subscript<typeof $${script}$>\n`;
|
402
|
-
for (const script of wildAnyScripts)
|
403
|
-
o += `\t${script}: (...args: any) => any\n`;
|
404
|
-
o += "}\n\ndeclare global {\n\tinterface PlayerFullsec {";
|
405
|
-
let lastWasMultiLine = true;
|
406
|
-
for (const user of users) {
|
407
|
-
const scripts = allScripts[user];
|
408
|
-
const anyScripts = allAnyScripts[user];
|
409
|
-
if ((scripts && scripts.length) || (anyScripts && anyScripts.length)) {
|
410
|
-
lastWasMultiLine = true;
|
411
|
-
o += `\n\t\t${user}: WildFullsec & {\n`;
|
412
|
-
for (const script of scripts)
|
413
|
-
o += `\t\t\t${script}: Subscript<typeof $${user}$${script}$>\n`;
|
414
|
-
for (const script of anyScripts)
|
415
|
-
o += `\t\t\t${script}: (...args: any) => any\n`;
|
416
|
-
o += "\t\t}";
|
417
|
-
}
|
418
|
-
else {
|
419
|
-
if (lastWasMultiLine) {
|
420
|
-
o += "\n";
|
421
|
-
lastWasMultiLine = false;
|
422
|
-
}
|
423
|
-
o += `\t\t${user}: WildFullsec`;
|
424
|
-
}
|
425
|
-
o += "\n";
|
426
|
-
}
|
427
|
-
o += "\t}\n}\n";
|
428
|
-
await writeFile(target, o);
|
429
|
-
}
|
430
|
-
exports.generateTypings = generateTypings;
|
431
|
-
/**
|
432
|
-
* Minifies a given script
|
433
|
-
*
|
434
|
-
* @param script JavaScript or TypeScript code
|
435
|
-
*/
|
436
|
-
async function processScript(script) {
|
437
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
438
|
-
let preScriptComments;
|
439
|
-
let autocomplete;
|
440
|
-
[, preScriptComments, script, autocomplete] = script.match(/((?:^\s*\/\/.*\n)*)\s*((?:.+?\/\/\s*(.+?)\s*$)?[^]*)/m);
|
441
|
-
if (!script)
|
442
|
-
throw new Error("script was empty");
|
443
|
-
if (script.match(/(?:SC|DB)\$/))
|
444
|
-
throw new Error("SC$ and DB$ are protected and cannot appear in a script");
|
445
|
-
let seclevel;
|
446
|
-
for (const line of preScriptComments.split("\n")) {
|
447
|
-
let [, autocompleteMatch, seclevelMatch] = (line.match(/^\s*\/\/\s*(?:@autocomplete\s*([^\s].*?)|@seclevel\s*([^\s].*?))\s*$/) || []);
|
448
|
-
if (autocompleteMatch)
|
449
|
-
autocomplete = autocompleteMatch;
|
450
|
-
else if (seclevelMatch) {
|
451
|
-
if (seclevelMatch.match(/^(?:fullsec|f|4|fs|full)$/i))
|
452
|
-
seclevel = 4;
|
453
|
-
else if (seclevelMatch.match(/^(?:highsec|h|3|hs|high)$/i))
|
454
|
-
seclevel = 3;
|
455
|
-
else if (seclevelMatch.match(/^(?:midsec|m|2|ms|mid)$/i))
|
456
|
-
seclevel = 2;
|
457
|
-
else if (seclevelMatch.match(/^(?:lowsec|l|1|ls|low)$/i))
|
458
|
-
seclevel = 1;
|
459
|
-
else if (seclevelMatch.match(/^(?:nullsec|n|0|ns|null)$/i))
|
460
|
-
seclevel = 0;
|
461
|
-
}
|
462
|
-
}
|
463
|
-
let detectedSeclevel;
|
464
|
-
if (script.match(/[#$][n0]s\.[a-z_][a-z_0-9]{0,24}\.[a-z_][a-z_0-9]{0,24}\(/))
|
465
|
-
detectedSeclevel = 0;
|
466
|
-
else if (script.match(/[#$][l1]s\.[a-z_][a-z_0-9]{0,24}\.[a-z_][a-z_0-9]{0,24}\(/))
|
467
|
-
detectedSeclevel = 1;
|
468
|
-
else if (script.match(/[#$][m2]s\.[a-z_][a-z_0-9]{0,24}\.[a-z_][a-z_0-9]{0,24}\(/))
|
469
|
-
detectedSeclevel = 2;
|
470
|
-
else if (script.match(/[#$][h3]s\.[a-z_][a-z_0-9]{0,24}\.[a-z_][a-z_0-9]{0,24}\(/))
|
471
|
-
detectedSeclevel = 3;
|
472
|
-
else if (script.match(/[#$][f4]s\.[a-z_][a-z_0-9]{0,24}\.[a-z_][a-z_0-9]{0,24}\(/))
|
473
|
-
detectedSeclevel = 4;
|
474
|
-
const seclevelNames = ["NULLSEC", "LOWSEC", "MIDSEC", "HIGHSEC", "FULLSEC"];
|
475
|
-
if (seclevel == undefined)
|
476
|
-
seclevel = (_a = seclevel !== null && seclevel !== void 0 ? seclevel : detectedSeclevel) !== null && _a !== void 0 ? _a : 0;
|
477
|
-
else if (detectedSeclevel != undefined && seclevel > detectedSeclevel)
|
478
|
-
throw new Error(`detected seclevel of ${seclevelNames[detectedSeclevel]} is lower than the provided seclevel of ${seclevelNames[seclevel]}`);
|
479
|
-
const semicolons = (_c = (_b = script.match(/;/g)) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0;
|
480
|
-
script = script
|
481
|
-
.replace(/#[fhmln43210]s\.scripts\.quine\(\)/g, JSON.stringify(script))
|
482
|
-
.replace(/[#$][fhmln43210]?s\.([a-z_][a-z_0-9]{0,24})\.([a-z_][a-z_0-9]{0,24})\(/g, "SC$$$1$$$2(")
|
483
|
-
.replace(/^function\s*\(/, "function script(")
|
484
|
-
.replace(/#D\(/g, "$D(")
|
485
|
-
.replace(/#FMCL/g, "$FMCL")
|
486
|
-
.replace(/#G/g, "$G")
|
487
|
-
.replace(/[#$]db\./g, "DB$");
|
488
|
-
// typescript compilation, this runs on regular javascript too to convert
|
489
|
-
// any post es2015 syntax into es2015 syntax
|
490
|
-
const { outputText, diagnostics = [] } = typescript_1.default.transpileModule(script, {
|
491
|
-
compilerOptions: { target: typescript_1.default.ScriptTarget.ES2015 },
|
492
|
-
reportDiagnostics: true
|
493
|
-
});
|
494
|
-
const warnings = diagnostics.map(({ messageText, start }) => ({
|
495
|
-
message: typeof messageText == "string" ? messageText : messageText.messageText,
|
496
|
-
line: (0, lib_1.positionToLineNumber)(start, script)
|
497
|
-
}));
|
498
|
-
script = outputText.replace(/^export /, "");
|
499
|
-
await writeFile("./test.json", JSON.stringify((0, esprima_1.parseScript)(script), null, "\t"));
|
500
|
-
const ast = (0, esprima_1.parseScript)(script);
|
501
|
-
for (const node of (0, esquery_1.query)(ast, "ClassBody > MethodDefinition[kind=constructor] > FunctionExpression > BlockStatement")) {
|
502
|
-
node.body.unshift({
|
503
|
-
type: "VariableDeclaration",
|
504
|
-
declarations: [
|
505
|
-
{
|
506
|
-
type: "VariableDeclarator",
|
507
|
-
id: {
|
508
|
-
type: "Identifier",
|
509
|
-
name: "__THIS__"
|
510
|
-
}
|
511
|
-
}
|
512
|
-
],
|
513
|
-
kind: "let"
|
514
|
-
});
|
515
|
-
}
|
516
|
-
for (const node of (0, esquery_1.query)(ast, "ClassBody > MethodDefinition[kind=constructor] > FunctionExpression > BlockStatement !CallExpression > Super")) {
|
517
|
-
const newNode = {
|
518
|
-
type: "AssignmentExpression",
|
519
|
-
operator: "=",
|
520
|
-
left: {
|
521
|
-
type: "Identifier",
|
522
|
-
name: "__THIS__"
|
523
|
-
},
|
524
|
-
right: { ...node }
|
525
|
-
};
|
526
|
-
Object.assign((0, lib_1.clearObject)(node), newNode);
|
527
|
-
}
|
528
|
-
for (const node of (0, esquery_1.query)(ast, "ClassBody > MethodDefinition > FunctionExpression > BlockStatement !ThisExpression")) {
|
529
|
-
const newNode = {
|
530
|
-
type: "Identifier",
|
531
|
-
name: "__THIS__"
|
532
|
-
};
|
533
|
-
Object.assign((0, lib_1.clearObject)(node), newNode);
|
534
|
-
}
|
535
|
-
for (const node of (0, esquery_1.query)(ast, "ClassBody > MethodDefinition[kind=method] > FunctionExpression > BlockStatement")) {
|
536
|
-
node.body.unshift({
|
537
|
-
type: "VariableDeclaration",
|
538
|
-
declarations: [{
|
539
|
-
type: "VariableDeclarator",
|
540
|
-
id: {
|
541
|
-
type: "Identifier",
|
542
|
-
name: "__THIS__"
|
543
|
-
},
|
544
|
-
init: {
|
545
|
-
type: "CallExpression",
|
546
|
-
callee: {
|
547
|
-
type: "MemberExpression",
|
548
|
-
computed: false,
|
549
|
-
object: {
|
550
|
-
type: "Super"
|
551
|
-
},
|
552
|
-
property: {
|
553
|
-
type: "Identifier",
|
554
|
-
name: "valueOf"
|
555
|
-
},
|
556
|
-
optional: false
|
557
|
-
},
|
558
|
-
arguments: [],
|
559
|
-
optional: false
|
560
|
-
}
|
561
|
-
}],
|
562
|
-
"kind": "let"
|
563
|
-
});
|
564
|
-
}
|
565
|
-
script = (0, escodegen_1.generate)(ast);
|
566
|
-
// the typescript inserts semicolons where they weren't already so we take
|
567
|
-
// all semicolons out of the count and add the number of semicolons in the
|
568
|
-
// source to make things fair
|
569
|
-
let srcLength = (0, lib_1.hackmudLength)(script.replace(/^function\s*\w+\(/, "function("))
|
570
|
-
- ((_e = (_d = script.match(/;/g)) === null || _d === void 0 ? void 0 : _d.length) !== null && _e !== void 0 ? _e : 0)
|
571
|
-
+ semicolons
|
572
|
-
+ ((_g = (_f = script.match(/SC\$[a-zA-Z_][a-zA-Z0-9_]*\$[a-zA-Z_][a-zA-Z0-9_]*\(/g)) === null || _f === void 0 ? void 0 : _f.length) !== null && _g !== void 0 ? _g : 0)
|
573
|
-
+ ((_j = (_h = script.match(/DB\$/g)) === null || _h === void 0 ? void 0 : _h.length) !== null && _j !== void 0 ? _j : 0);
|
574
|
-
// remove dead code (so we don't waste chracters quine cheating strings
|
575
|
-
// that aren't even used)
|
576
|
-
script = (await (0, terser_1.minify)(script, {
|
577
|
-
ecma: 2015,
|
578
|
-
parse: { bare_returns: true },
|
579
|
-
compress: { booleans: false }
|
580
|
-
})).code || "";
|
581
|
-
let blockStatementIndex;
|
582
|
-
if (script.startsWith("function "))
|
583
|
-
blockStatementIndex = getFunctionBodyStart(script);
|
584
|
-
else {
|
585
|
-
script = `function script(context, args) {\n${script}\n}`;
|
586
|
-
blockStatementIndex = 31;
|
587
|
-
srcLength += 24;
|
588
|
-
}
|
589
|
-
let scriptBeforeJSONValueReplacement = (await (0, terser_1.minify)(script, {
|
590
|
-
ecma: 2015,
|
591
|
-
compress: {
|
592
|
-
passes: Infinity,
|
593
|
-
unsafe: true,
|
594
|
-
unsafe_arrows: true,
|
595
|
-
unsafe_comps: true,
|
596
|
-
unsafe_symbols: true,
|
597
|
-
unsafe_methods: true,
|
598
|
-
unsafe_proto: true,
|
599
|
-
unsafe_regexp: true,
|
600
|
-
unsafe_undefined: true
|
601
|
-
},
|
602
|
-
format: { semicolons: false }
|
603
|
-
})).code || "";
|
604
|
-
{
|
605
|
-
const tokens = [...(0, acorn_1.tokenizer)(scriptBeforeJSONValueReplacement, { ecmaVersion: 2015 })].reverse().values();
|
606
|
-
for (const token of tokens) {
|
607
|
-
// we can't replace any tokens before the block statement or we'll break stuff
|
608
|
-
if (token.start < blockStatementIndex)
|
609
|
-
break;
|
610
|
-
switch (token.type) {
|
611
|
-
case acorn_1.tokTypes.name:
|
612
|
-
{
|
613
|
-
if (token.value != "prototype" && token.value != "__proto__")
|
614
|
-
break;
|
615
|
-
const tokenBefore = tokens.next().value;
|
616
|
-
if (tokenBefore.type != acorn_1.tokTypes.dot)
|
617
|
-
break;
|
618
|
-
srcLength += 3;
|
619
|
-
scriptBeforeJSONValueReplacement = (0, lib_1.stringSplice)(scriptBeforeJSONValueReplacement, `["${token.value}"]`, tokenBefore.start, token.end);
|
620
|
-
}
|
621
|
-
break;
|
622
|
-
case acorn_1.tokTypes._const:
|
623
|
-
{
|
624
|
-
scriptBeforeJSONValueReplacement = (0, lib_1.stringSplice)(scriptBeforeJSONValueReplacement, "let", token.start, token.end);
|
625
|
-
}
|
626
|
-
break;
|
627
|
-
case acorn_1.tokTypes._this:
|
628
|
-
throw new Error('"this" keyword is not supported in hackmud');
|
629
|
-
}
|
630
|
-
}
|
631
|
-
}
|
632
|
-
const jsonValues = [];
|
633
|
-
let undefinedIsReferenced = false;
|
634
|
-
// we iterate through the tokens backwards so that substring replacements
|
635
|
-
// don't affect future replacements since a part of the string could be
|
636
|
-
// replaced with a string of a different length which messes up indexes
|
637
|
-
const tokens = [...(0, acorn_1.tokenizer)(script, { ecmaVersion: 2015 })].reverse().values();
|
638
|
-
let templateToRightOfPlaceholder = false;
|
639
|
-
for (const token of tokens) {
|
640
|
-
// we can't replace any tokens before the block statement or we'll break stuff
|
641
|
-
if (token.start < blockStatementIndex)
|
642
|
-
break;
|
643
|
-
switch (token.type) {
|
644
|
-
case acorn_1.tokTypes.backQuote:
|
645
|
-
{
|
646
|
-
const templateToken = tokens.next().value;
|
647
|
-
if (tokens.next().value.type == acorn_1.tokTypes.backQuote)
|
648
|
-
throw new Error("tagged templates not supported yet");
|
649
|
-
// no point in concatenating an empty string
|
650
|
-
if (templateToken.value == "") {
|
651
|
-
script = (0, lib_1.stringSplice)(script, "))", templateToken.start - 1, token.end);
|
652
|
-
break;
|
653
|
-
}
|
654
|
-
let jsonValueIndex = jsonValues.indexOf(templateToken.value);
|
655
|
-
if (jsonValueIndex == -1)
|
656
|
-
jsonValueIndex += jsonValues.push(templateToken.value);
|
657
|
-
script = (0, lib_1.stringSplice)(script, `)+_JSON_VALUE_${jsonValueIndex}_)`, templateToken.start - 1, token.end);
|
658
|
-
}
|
659
|
-
break;
|
660
|
-
case acorn_1.tokTypes.template:
|
661
|
-
{
|
662
|
-
if (tokens.next().value.type == acorn_1.tokTypes.backQuote) {
|
663
|
-
if (tokens.next().value.type == acorn_1.tokTypes.name)
|
664
|
-
throw new Error("tagged templates not supported yet");
|
665
|
-
// there *is* a point in concatenating an empty string at the
|
666
|
-
// start because foo + bar is not the same thing as "" + foo + bar
|
667
|
-
// ...but foo + "<template>" + bar *is* the same thing as "" + foo + "<template>" + bar
|
668
|
-
// so we just need to check if there's a template to the right of the placeholder and skip that case
|
669
|
-
if (token.value == "" && templateToRightOfPlaceholder) {
|
670
|
-
templateToRightOfPlaceholder = false;
|
671
|
-
script = (0, lib_1.stringSplice)(script, "((", token.start - 1, token.end + 2);
|
672
|
-
break;
|
673
|
-
}
|
674
|
-
templateToRightOfPlaceholder = false;
|
675
|
-
let jsonValueIndex = jsonValues.indexOf(token.value);
|
676
|
-
if (jsonValueIndex == -1)
|
677
|
-
jsonValueIndex += jsonValues.push(token.value);
|
678
|
-
script = (0, lib_1.stringSplice)(script, `(_JSON_VALUE_${jsonValueIndex}_+(`, token.start - 1, token.end + 2);
|
679
|
-
break;
|
680
|
-
}
|
681
|
-
// no point in concatenating an empty string
|
682
|
-
if (token.value == "") {
|
683
|
-
templateToRightOfPlaceholder = false;
|
684
|
-
script = (0, lib_1.stringSplice)(script, ")+(", token.start - 1, token.end + 2);
|
685
|
-
break;
|
686
|
-
}
|
687
|
-
templateToRightOfPlaceholder = true;
|
688
|
-
let jsonValueIndex = jsonValues.indexOf(token.value);
|
689
|
-
if (jsonValueIndex == -1)
|
690
|
-
jsonValueIndex += jsonValues.push(token.value);
|
691
|
-
script = (0, lib_1.stringSplice)(script, `)+_JSON_VALUE_${jsonValueIndex}_+(`, token.start - 1, token.end + 2);
|
692
|
-
}
|
693
|
-
break;
|
694
|
-
case acorn_1.tokTypes.name:
|
695
|
-
{
|
696
|
-
if (token.value.length < 3)
|
697
|
-
break;
|
698
|
-
const tokenBefore = tokens.next().value;
|
699
|
-
if (tokenBefore.type == acorn_1.tokTypes.dot) {
|
700
|
-
let jsonValueIndex = jsonValues.indexOf(token.value);
|
701
|
-
if (jsonValueIndex == -1)
|
702
|
-
jsonValueIndex += jsonValues.push(token.value);
|
703
|
-
script = (0, lib_1.stringSplice)(script, `[_JSON_VALUE_${jsonValueIndex}_]`, tokenBefore.start, token.end);
|
704
|
-
break;
|
705
|
-
}
|
706
|
-
if (token.value == "undefined") {
|
707
|
-
script = (0, lib_1.stringSplice)(script, " _UNDEFINED_ ", token.start, token.end);
|
708
|
-
undefinedIsReferenced = true;
|
709
|
-
}
|
710
|
-
}
|
711
|
-
break;
|
712
|
-
case acorn_1.tokTypes._null:
|
713
|
-
{
|
714
|
-
let jsonValueIndex = jsonValues.indexOf(null);
|
715
|
-
if (jsonValueIndex == -1)
|
716
|
-
jsonValueIndex += jsonValues.push(null);
|
717
|
-
script = (0, lib_1.stringSplice)(script, ` _JSON_VALUE_${jsonValueIndex}_ `, token.start, token.end);
|
718
|
-
}
|
719
|
-
break;
|
720
|
-
case acorn_1.tokTypes._true:
|
721
|
-
{
|
722
|
-
let jsonValueIndex = jsonValues.indexOf(true);
|
723
|
-
if (jsonValueIndex == -1)
|
724
|
-
jsonValueIndex += jsonValues.push(true);
|
725
|
-
script = (0, lib_1.stringSplice)(script, ` _JSON_VALUE_${jsonValueIndex}_ `, token.start, token.end);
|
726
|
-
}
|
727
|
-
break;
|
728
|
-
case acorn_1.tokTypes._false:
|
729
|
-
{
|
730
|
-
let jsonValueIndex = jsonValues.indexOf(false);
|
731
|
-
if (jsonValueIndex == -1)
|
732
|
-
jsonValueIndex += jsonValues.push(false);
|
733
|
-
script = (0, lib_1.stringSplice)(script, ` _JSON_VALUE_${jsonValueIndex}_ `, token.start, token.end);
|
734
|
-
}
|
735
|
-
break;
|
736
|
-
case acorn_1.tokTypes.num:
|
737
|
-
{
|
738
|
-
if (token.value == 0) {
|
739
|
-
const tokenBefore = tokens.next().value;
|
740
|
-
if (tokenBefore.type == acorn_1.tokTypes._void) {
|
741
|
-
script = (0, lib_1.stringSplice)(script, " _UNDEFINED_ ", tokenBefore.start, token.end);
|
742
|
-
undefinedIsReferenced = true;
|
743
|
-
}
|
744
|
-
// may as well break here since we're gonna break anyway
|
745
|
-
break;
|
746
|
-
}
|
747
|
-
if (token.value < 10)
|
748
|
-
break;
|
749
|
-
let jsonValueIndex = jsonValues.indexOf(token.value);
|
750
|
-
if (jsonValueIndex == -1)
|
751
|
-
jsonValueIndex += jsonValues.push(token.value);
|
752
|
-
script = (0, lib_1.stringSplice)(script, ` _JSON_VALUE_${jsonValueIndex}_ `, token.start, token.end);
|
753
|
-
}
|
754
|
-
break;
|
755
|
-
case acorn_1.tokTypes.string:
|
756
|
-
{
|
757
|
-
if (token.value.includes("\u0000"))
|
758
|
-
break;
|
759
|
-
let jsonValueIndex = jsonValues.indexOf(token.value);
|
760
|
-
if (jsonValueIndex == -1)
|
761
|
-
jsonValueIndex += jsonValues.push(token.value);
|
762
|
-
script = (0, lib_1.stringSplice)(script, ` _JSON_VALUE_${jsonValueIndex}_ `, token.start, token.end);
|
763
|
-
}
|
764
|
-
break;
|
765
|
-
case acorn_1.tokTypes._const:
|
766
|
-
{
|
767
|
-
script = (0, lib_1.stringSplice)(script, "let", token.start, token.end);
|
768
|
-
}
|
769
|
-
break;
|
770
|
-
case acorn_1.tokTypes._this:
|
771
|
-
throw new Error('"this" keyword is not supported in hackmud');
|
772
|
-
}
|
773
|
-
}
|
774
|
-
let comment = null;
|
775
|
-
let hasComment = false;
|
776
|
-
if (jsonValues.length) {
|
777
|
-
hasComment = true;
|
778
|
-
if (jsonValues.length == 1) {
|
779
|
-
if (typeof jsonValues[0] == "string" && !jsonValues[0].includes("\n") && !jsonValues[0].includes("\t")) {
|
780
|
-
script = (0, lib_1.stringSplice)(script, `\nlet _JSON_VALUE_0_ = SC$scripts$quine().split\`\t\`[_SPLIT_INDEX_]${undefinedIsReferenced ? ", _UNDEFINED_" : ""}\n`, blockStatementIndex + 1);
|
781
|
-
comment = jsonValues[0];
|
782
|
-
}
|
783
|
-
else {
|
784
|
-
script = (0, lib_1.stringSplice)(script, `\nlet _JSON_VALUE_0_ = JSON.parse(SC$scripts$quine().split\`\t\`[_SPLIT_INDEX_])${undefinedIsReferenced ? ", _UNDEFINED_" : ""}\n`, blockStatementIndex + 1);
|
785
|
-
comment = JSON.stringify(jsonValues[0]);
|
786
|
-
}
|
787
|
-
}
|
788
|
-
else {
|
789
|
-
script = (0, lib_1.stringSplice)(script, `\nlet [ ${jsonValues.map((_, i) => `_JSON_VALUE_${i}_`).join(", ")} ] = JSON.parse(SC$scripts$quine().split\`\t\`[_SPLIT_INDEX_])${undefinedIsReferenced ? ", _UNDEFINED_" : ""}\n`, blockStatementIndex + 1);
|
790
|
-
comment = JSON.stringify(jsonValues);
|
791
|
-
}
|
792
|
-
}
|
793
|
-
else
|
794
|
-
script = script.replace(/_UNDEFINED_/g, "void 0");
|
795
|
-
script = (await (0, terser_1.minify)(script, {
|
796
|
-
ecma: 2015,
|
797
|
-
compress: {
|
798
|
-
passes: Infinity,
|
799
|
-
unsafe: true,
|
800
|
-
unsafe_arrows: true,
|
801
|
-
unsafe_comps: true,
|
802
|
-
unsafe_symbols: true,
|
803
|
-
unsafe_methods: true,
|
804
|
-
unsafe_proto: true,
|
805
|
-
unsafe_regexp: true,
|
806
|
-
unsafe_undefined: true
|
807
|
-
},
|
808
|
-
format: { semicolons: false }
|
809
|
-
})).code || "";
|
810
|
-
// this step affects the chracter count and can't be done after the count comparison
|
811
|
-
if (comment != null) {
|
812
|
-
script = (0, lib_1.stringSplice)(script, `${autocomplete ? `//${autocomplete}\n` : ""}\n//\t${comment}\t\n`, getFunctionBodyStart(script) + 1);
|
813
|
-
for (const [i, part] of script.split("\t").entries()) {
|
814
|
-
if (part != comment)
|
815
|
-
continue;
|
816
|
-
script = script.replace("_SPLIT_INDEX_", (await (0, terser_1.minify)(`$(${i})`, { ecma: 2015 })).code.match(/\$\((.+)\)/)[1]);
|
817
|
-
break;
|
818
|
-
}
|
819
|
-
}
|
820
|
-
// if the script has a comment, it's gonna contain `SC$scripts$quine()`
|
821
|
-
// which is gonna eventually compile to `#fs.scripts.quine()` which contains
|
822
|
-
// an extra character so we have to account for that
|
823
|
-
if ((0, lib_1.hackmudLength)(scriptBeforeJSONValueReplacement) <= ((0, lib_1.hackmudLength)(script) + Number(hasComment))) {
|
824
|
-
script = scriptBeforeJSONValueReplacement;
|
825
|
-
if (autocomplete)
|
826
|
-
script = (0, lib_1.stringSplice)(script, `//${autocomplete}\n`, getFunctionBodyStart(script) + 1);
|
827
|
-
}
|
828
|
-
script = script
|
829
|
-
.replace(/^function\s*\w+\(/, "function(")
|
830
|
-
.replace(/SC\$([a-zA-Z_][a-zA-Z0-9_]*)\$([a-zA-Z_][a-zA-Z0-9_]*)\(/g, `#${"nlmhf"[seclevel]}s.$1.$2(`)
|
831
|
-
.replace(/\$D\(/g, "#D(")
|
832
|
-
.replace(/\$FMCL/g, "#FMCL")
|
833
|
-
.replace(/\$G/g, "#G")
|
834
|
-
.replace(/DB\$/g, "#db.");
|
835
|
-
return {
|
836
|
-
srcLength,
|
837
|
-
script,
|
838
|
-
warnings
|
839
|
-
};
|
840
|
-
}
|
841
|
-
exports.processScript = processScript;
|
842
|
-
function getFunctionBodyStart(code) {
|
843
|
-
const tokens = (0, acorn_1.tokenizer)(code, { ecmaVersion: 2015 });
|
844
|
-
tokens.getToken(); // function
|
845
|
-
tokens.getToken(); // name
|
846
|
-
tokens.getToken(); // (
|
847
|
-
let nests = 1;
|
848
|
-
while (nests) {
|
849
|
-
const token = tokens.getToken();
|
850
|
-
if (token.type == acorn_1.tokTypes.parenL)
|
851
|
-
nests++;
|
852
|
-
else if (token.type == acorn_1.tokTypes.parenR)
|
853
|
-
nests--;
|
854
|
-
}
|
855
|
-
return tokens.getToken().start; // {
|
856
|
-
}
|
857
|
-
exports.getFunctionBodyStart = getFunctionBodyStart;
|
1
|
+
import 'acorn';
|
2
|
+
import 'chokidar';
|
3
|
+
import 'escodegen';
|
4
|
+
import 'esprima';
|
5
|
+
import 'esquery';
|
6
|
+
import 'fs';
|
7
|
+
import 'path';
|
8
|
+
import 'terser';
|
9
|
+
import 'typescript';
|
10
|
+
export { g as generateTypings, e as getFunctionBodyStart, p as processScript, b as pull, d as push, s as supportedExtensions, a as syncMacros, t as test, c as watch } from './shared.js';
|