hackmud-script-manager 0.19.0-f21e319 → 0.19.0-fa82f73

Sign up to get free protection for your applications and to get access to all the features.
package/bin/hsm.js CHANGED
@@ -1,688 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import { DynamicMap } from '@samual/lib/DynamicMap';
3
- import { assert } from '@samual/lib/assert';
4
- import { countHackmudCharacters } from '@samual/lib/countHackmudCharacters';
5
- import { writeFilePersistent } from '@samual/lib/writeFilePersistent';
6
- import { readFile, writeFile, mkdir, rmdir } from 'fs/promises';
7
- import { homedir } from 'os';
8
- import { supportedExtensions } from '../constants.js';
9
- import { generateTypeDeclaration } from '../generateTypeDeclaration.js';
10
- import { pull } from '../pull.js';
11
- import { syncMacros } from '../syncMacros.js';
12
- import { resolve, extname, basename, dirname, relative } from 'path';
13
- import '@samual/lib/copyFilePersistent';
14
-
15
- var version = "0.19.0-f21e319";
16
-
17
- /* eslint-disable unicorn/no-process-exit */
18
-
19
- /* | ArgValue[]*/
20
-
21
- const configDirectoryPath = resolve(homedir(), `.config`);
22
- const configFilePath = resolve(configDirectoryPath, `hsm.json`);
23
- const options = new Map();
24
- const commands = [];
25
- const userColours = new DynamicMap(user => {
26
- let hash = 0;
27
- for (const char of user) hash += (hash >> 1) + hash + `xi1_8ratvsw9hlbgm02y5zpdcn7uekof463qj`.indexOf(char) + 1;
28
- return [colourJ, colourK, colourM, colourW, colourL, colourB][hash % 6](user);
29
- });
30
- const logNeedHackmudPathMessage = () => console.error(colourS(`\
31
- ${colourD(`You need to set hackmudPath in config before you can use this command`)}
32
-
33
- ${colourA(`To fix this:`)}
34
- Open hackmud and run "${colourC(`#dir`)}"
35
- This will open a file browser and print your hackmud user's script directory
36
- Go up 2 directories and then copy the path
37
- Then in a terminal run "${colourC(`hsm`)} ${colourL(`config set`)} ${colourV(`hackmudPath`)} ${colourB(`<the path you copied>`)}"`));
38
- const logHelp = () => {
39
- const pushCommandDescription = `Push scripts from a directory to hackmud user's scripts directories`;
40
- const watchCommandDescription = `Watch a directory and push a script when modified`;
41
- const minifyCommandDescription = `Minify a script file on the spot`;
42
- const generateTypeDeclarationCommandDescription = `Generate a type declaration file for a directory of scripts`;
43
- const syncMacrosCommandDescription = `Sync macros across all hackmud users`;
44
- const configCommandDescription = `Modify and view the config file`;
45
- const configGetCommandDescription = `Retrieve a value from the config file`;
46
- const configSetCommandDescription = `Assign a value to the config file`;
47
- const configDeleteCommandDescription = `Remove a key and value from the config file`;
48
- const pullCommandDescription = `Pull a script a from a hackmud user's script directory`;
49
- const skipMinifyOptionDescription = `Skip minification to produce a readable script`;
50
- const mangleNamesOptionDescription = `Reduce character count further but lose function names in error call stacks`;
51
- const forceQuineCheatsOptionDescription = `Force quine cheats even if the character count is higher`;
52
- console.log(colourN(`Version`) + colourS(`: `) + colourV(version));
53
- switch (commands[0]) {
54
- case `config`:
55
- {
56
- switch (commands[1]) {
57
- case `get`:
58
- {
59
- console.log(`
60
- ${colourJ(configGetCommandDescription)}
61
-
62
- ${colourA(`Usage:`)}
63
- ${colourC(`hsm`)} ${colourL(`${commands[0]} ${commands[1]}`)} ${colourB(`<key>`)}`);
64
- }
65
- break;
66
- case `set`:
67
- {
68
- console.log(`
69
- ${colourJ(configSetCommandDescription)}
70
-
71
- ${colourA(`Usage:`)}
72
- ${colourC(`hsm`)} ${colourL(`${commands[0]} ${commands[1]}`)} ${colourB(`<key> <value>`)}`);
73
- }
74
- break;
75
- case `delete`:
76
- {
77
- console.log(`
78
- ${colourJ(configDeleteCommandDescription)}
79
-
80
- ${colourA(`Usage:`)}
81
- ${colourC(`hsm`)} ${colourL(`${commands[0]} ${commands[1]}`)} ${colourB(`<key>`)}`);
82
- }
83
- break;
84
- default:
85
- {
86
- console.log(colourS(`\
87
- ${colourN(`Config path`)}: ${colourV(configFilePath)}
88
-
89
- ${colourJ(`Modify the config file`)}
90
-
91
- ${colourA(`Usage:`)}
92
- ${colourC(`hsm`)} ${colourL(`${commands[0]} get`)} ${colourB(`<key>`)}
93
- ${configGetCommandDescription}
94
- ${colourC(`hsm`)} ${colourL(`${commands[0]} set`)} ${colourB(`<key> <value>`)}
95
- ${configSetCommandDescription}
96
- ${colourC(`hsm`)} ${colourL(`${commands[0]} delete`)} ${colourB(`<key>`)}
97
- ${configDeleteCommandDescription}`));
98
- }
99
- }
100
- }
101
- break;
102
- case `push`:
103
- {
104
- console.log(colourS(`
105
- ${colourJ(pushCommandDescription)}
106
-
107
- ${colourA(`Usage:`)}
108
- ${colourC(`hsm`)} ${colourL(commands[0])} ${colourB(`<directory> [<script user>.<script name>...]`)}
109
-
110
- ${colourA(`Options:`)}
111
- ${colourN(`--skip-minify`)}
112
- ${skipMinifyOptionDescription}
113
- ${colourN(`--mangle-names`)}
114
- ${mangleNamesOptionDescription}
115
- ${colourN(`--force-quine-cheats`)}
116
- ${forceQuineCheatsOptionDescription}`));
117
- }
118
- break;
119
- case `dev`:
120
- case `watch`:
121
- {
122
- console.log(colourS(`\
123
- ${colourN(`Aliases`)}: ${colourV(`watch, dev`)}
124
-
125
- ${colourJ(watchCommandDescription)}
126
-
127
- ${colourA(`Usage:`)}
128
- ${colourC(`hsm`)} ${colourL(commands[0])} ${colourB(`<directory> [<script user>.<script name>...]`)}
129
-
130
- ${colourA(`Options:`)}
131
- ${colourN(`--skip-minify`)}
132
- ${skipMinifyOptionDescription}
133
- ${colourN(`--mangle-names`)}
134
- ${mangleNamesOptionDescription}
135
- ${colourN(`--type-declaration-path`)}=${colourB(`<path>`)}
136
- Path to generate a type declaration file for the scripts
137
- ${colourN(`--force-quine-cheats`)}
138
- ${forceQuineCheatsOptionDescription}`));
139
- }
140
- break;
141
- case `pull`:
142
- {
143
- console.log(colourS(`
144
- ${colourJ(pullCommandDescription)}
145
-
146
- ${colourA(`Usage:`)}
147
- ${colourC(`hsm`)} ${colourL(commands[0])} ${colourB(`<script user>`)}${colourV(`.`)}${colourB(`<script name>`)}`));
148
- }
149
- break;
150
- case `minify`:
151
- case `golf`:
152
- {
153
- console.log(colourS(`\
154
- ${colourN(`Aliases`)}: ${colourV(`minify, golf`)}
155
-
156
- ${colourJ(minifyCommandDescription)}
157
-
158
- ${colourA(`Usage:`)}
159
- ${colourC(`hsm`)} ${colourL(commands[0])} ${colourB(`<target> [output path]`)}
160
-
161
- ${colourA(`Options:`)}
162
- ${colourN(`--skip-minify`)}
163
- ${skipMinifyOptionDescription}
164
- ${colourN(`--mangle-names`)}
165
- ${mangleNamesOptionDescription}
166
- ${colourN(`--force-quine-cheats`)}
167
- ${forceQuineCheatsOptionDescription}
168
- ${colourN(`--watch`)}
169
- Watch for changes`));
170
- }
171
- break;
172
- case `generate-type-declaration`:
173
- case `gen-type-declaration`:
174
- case `gen-dts`:
175
- case `gen-types`:
176
- {
177
- console.log(colourS(`\
178
- ${colourN(`Aliases`)}: ${colourV(`generate-type-declaration, gen-type-declaration, gen-types, gen-dts`)}
179
-
180
- ${colourJ(generateTypeDeclarationCommandDescription)}
181
-
182
- ${colourA(`Usage:`)}
183
- ${colourC(`hsm`)} ${colourL(commands[0])} ${colourB(`<directory> [output path]`)}`));
184
- }
185
- break;
186
- case `sync-macros`:
187
- {
188
- console.log(`\n${colourJ(syncMacrosCommandDescription)}`);
189
- }
190
- break;
191
- default:
192
- {
193
- console.log(colourS(`
194
- ${colourJ(`Hackmud Script Manager`)}
195
-
196
- ${colourA(`Commands:`)}
197
- ${colourL(`push`)}
198
- ${pushCommandDescription}
199
- ${colourL(`watch`)}, ${colourL(`dev`)}
200
- ${watchCommandDescription}
201
- ${colourL(`minify`)}, ${colourL(`golf`)}
202
- ${minifyCommandDescription}
203
- ${colourL(`generate-type-declaration`)}, ${colourL(`gen-type-declaration`)}, ${colourL(`gen-types`)}, ${colourL(`gen-dts`)}
204
- ${generateTypeDeclarationCommandDescription}
205
- ${colourL(`sync-macros`)}
206
- ${syncMacrosCommandDescription}
207
- ${colourL(`config`)}
208
- ${configCommandDescription}
209
- ${colourL(`pull`)}
210
- ${pullCommandDescription}`));
211
- }
212
- }
213
- };
214
- const exploreObject = (object, keys, createPath = false) => {
215
- for (const key of keys) {
216
- var _object;
217
- if (createPath) object = typeof object[key] == `object` ? object[key] : object[key] = {};else object = (_object = object) === null || _object === void 0 ? void 0 : _object[key];
218
- }
219
- return object;
220
- };
221
- const updateConfig = async config => {
222
- const json = JSON.stringify(config, undefined, `\t`);
223
- if (configDidNotExist) log(`Creating config file at ${configFilePath}`);
224
- await writeFile(configFilePath, json).catch(async error => {
225
- switch (error.code) {
226
- case `EISDIR`:
227
- {
228
- await rmdir(configFilePath);
229
- }
230
- break;
231
- case `ENOENT`:
232
- {
233
- await mkdir(configDirectoryPath);
234
- }
235
- break;
236
- default:
237
- throw error;
238
- }
239
- await writeFile(configFilePath, json);
240
- });
241
- };
242
- const logInfo = ({
243
- file,
244
- users,
245
- minLength,
246
- error
247
- }, hackmudPath) => {
248
- if (error) {
249
- logError(`error "${chalk.bold(error.message)}" in ${chalk.bold(file)}`);
250
- return;
251
- }
252
- console.log(`pushed ${chalk.bold(file)} to ${users.map(user => chalk.bold(userColours.get(user))).join(`, `)} | ${chalk.bold(String(minLength))} chars | ${chalk.bold(`${resolve(hackmudPath, users[0], `scripts`, basename(file, extname(file)))}.js`)}`);
253
- };
254
- const log = message => {
255
- console.log(colourS(message));
256
- };
257
- const logError = message => {
258
- console.error(colourD(message));
259
- process.exitCode = 1;
260
- };
261
- for (const argument of process.argv.slice(2)) {
262
- if (argument[0] == `-`) {
263
- const [key, valueRaw] = argument.split(`=`);
264
- let value = valueRaw;
265
- if (value) {
266
- if (value == `true`) value = true;else if (value == `false`) value = false;else {
267
- const number = Number(value);
268
- if (isFinite(number)) value = number;
269
- }
270
- } else value = true;
271
- if (argument[1] == `-`) options.set(key.slice(2), value);else {
272
- for (const option of key.slice(1)) options.set(option, value);
273
- }
274
- } else commands.push(argument);
275
- }
276
- if (commands[0] == `v` || commands[0] == `version` || options.get(`version`) || options.get(`v`)) {
277
- console.log(version);
278
- process.exit();
279
- }
280
- let configDidNotExist = false;
281
- const configPromise = readFile(configFilePath, {
282
- encoding: `utf-8`
283
- }).then(configFile => {
284
- let temporaryConfig;
285
- try {
286
- temporaryConfig = JSON.parse(configFile);
287
- } catch {
288
- // TODO log to error log file
289
- log(`Config file was corrupted, resetting`);
290
- return {};
291
- }
292
- if (!temporaryConfig || typeof temporaryConfig != `object`) {
293
- log(`Config file was corrupted, resetting`);
294
- return {};
295
- }
296
- if (`hackmudPath` in temporaryConfig && typeof temporaryConfig.hackmudPath != `string`) {
297
- log(`Property "hackmudPath" of config file was corrupted, removing`);
298
- delete temporaryConfig.hackmudPath;
299
- }
300
- return temporaryConfig;
301
- }, () => {
302
- configDidNotExist = true;
303
- return {};
304
- });
305
- const pushModule = import('../push.js');
306
- const processScriptModule = import('../processScript/index.js');
307
- const watchModule = import('../watch.js');
308
- const chokidarModule = import('chokidar');
309
- const {
310
- default: chalk
311
- } = await import('chalk');
312
- const colourA = chalk.rgb(0xFF, 0xFF, 0xFF);
313
- const colourB = chalk.rgb(0xCA, 0xCA, 0xCA);
314
- const colourC = chalk.rgb(0x9B, 0x9B, 0x9B);
315
- const colourD = chalk.rgb(0xFF, 0x00, 0x00);
316
- const colourJ = chalk.rgb(0xFF, 0xF4, 0x04);
317
- const colourK = chalk.rgb(0xF3, 0xF9, 0x98);
318
- const colourL = chalk.rgb(0x1E, 0xFF, 0x00);
319
- const colourM = chalk.rgb(0xB3, 0xFF, 0x9B);
320
- const colourN = chalk.rgb(0x00, 0xFF, 0xFF);
321
- const colourS = chalk.rgb(0x7A, 0xB2, 0xF4);
322
- const colourV = chalk.rgb(0xFF, 0x00, 0xEC);
323
- const colourW = chalk.rgb(0xFF, 0x96, 0xE0);
324
- if (options.get(`help`) || options.get(`h`)) {
325
- logHelp();
326
- process.exit();
327
- }
328
- let autoExit = true;
329
- switch (commands[0]) {
330
- case `push`:
331
- {
332
- const {
333
- hackmudPath
334
- } = await configPromise;
335
- if (!hackmudPath) {
336
- logNeedHackmudPathMessage();
337
- break;
338
- }
339
- const sourcePath = commands[1];
340
- if (!sourcePath) {
341
- logError(`Must provide the directory to push from\n`);
342
- logHelp();
343
- break;
344
- }
345
- const scripts = commands.slice(2);
346
- if (scripts.length) {
347
- const invalidScript = scripts.find(script => !/^(?:[a-z_][a-z\d_]{0,24}|\*)\.(?:[a-z_][a-z\d_]{0,24}|\*)$/.test(script));
348
- if (invalidScript) {
349
- logError(`Invalid script name: ${JSON.stringify(invalidScript)}\n`);
350
- logHelp();
351
- break;
352
- }
353
- } else scripts.push(`*.*`);
354
- if (options.has(`skip-minify`) && options.has(`mangle-names`)) {
355
- logError(`Option ${colourN(`--mangle-names`)} is not compatible with ${colourN(`--skip-minify`)}\n`);
356
- logHelp();
357
- break;
358
- }
359
- const shouldSkipMinify = options.get(`skip-minify`);
360
- let shouldMinify;
361
- if (shouldSkipMinify != undefined) {
362
- if (typeof shouldSkipMinify != `boolean`) {
363
- logError(`The value for ${colourN(`--skip-minify`)} must be ${colourV(`true`)} or ${colourV(`false`)}\n`);
364
- logHelp();
365
- break;
366
- }
367
- shouldMinify = !shouldSkipMinify;
368
- }
369
- const shouldMangleNames = options.get(`mangle-names`);
370
- if (shouldMangleNames != undefined && typeof shouldMangleNames != `boolean`) {
371
- logError(`The value for ${colourN(`--mangle-names`)} must be ${colourV(`true`)} or ${colourV(`false`)}\n`);
372
- logHelp();
373
- break;
374
- }
375
- const shouldforceQuineCheats = options.get(`force-quine-cheats`);
376
- if (shouldforceQuineCheats != undefined && typeof shouldforceQuineCheats != `boolean`) {
377
- logError(`The value for ${colourN(`--force-quine-cheats`)} must be ${colourV(`true`)} or ${colourV(`false`)}\n`);
378
- logHelp();
379
- break;
380
- }
381
- const {
382
- push
383
- } = await pushModule;
384
- const infos = await push(sourcePath, hackmudPath, {
385
- scripts,
386
- onPush: info => logInfo(info, hackmudPath),
387
- minify: shouldMinify,
388
- mangleNames: shouldMangleNames,
389
- forceQuineCheats: shouldforceQuineCheats
390
- });
391
- if (!infos.length) logError(`Could not find any scripts to push`);
392
- }
393
- break;
394
- case `dev`:
395
- case `watch`:
396
- {
397
- var _ref;
398
- const {
399
- hackmudPath
400
- } = await configPromise;
401
- if (!hackmudPath) {
402
- logNeedHackmudPathMessage();
403
- break;
404
- }
405
- const sourcePath = commands[1];
406
- if (!sourcePath) {
407
- logError(`Must provide the directory to watch\n`);
408
- logHelp();
409
- break;
410
- }
411
- const scripts = commands.slice(2);
412
- if (scripts.length) {
413
- const invalidScript = scripts.find(script => !/^(?:[a-z_][a-z\d_]{0,24}|\*)\.(?:[a-z_][a-z\d_]{0,24}|\*)$/.test(script));
414
- if (invalidScript) {
415
- logError(`Invalid script name: ${JSON.stringify(invalidScript)}\n`);
416
- logHelp();
417
- break;
418
- }
419
- } else scripts.push(`*.*`);
420
- if (options.has(`skip-minify`) && options.has(`mangle-names`)) {
421
- logError(`Option ${colourN(`--mangle-names`)} is not compatible with ${colourN(`--skip-minify`)}\n`);
422
- logHelp();
423
- break;
424
- }
425
- const shouldSkipMinify = options.get(`skip-minify`);
426
- let shouldMinify;
427
- if (shouldSkipMinify != undefined) {
428
- if (typeof shouldSkipMinify != `boolean`) {
429
- logError(`The value for ${colourN(`--skip-minify`)} must be ${colourV(`true`)} or ${colourV(`false`)}\n`);
430
- logHelp();
431
- break;
432
- }
433
- shouldMinify = !shouldSkipMinify;
434
- }
435
- const shouldMangleNames = options.get(`mangle-names`);
436
- if (shouldMangleNames != undefined && typeof shouldMangleNames != `boolean`) {
437
- logError(`The value for ${colourN(`--mangle-names`)} must be ${colourV(`true`)} or ${colourV(`false`)}\n`);
438
- logHelp();
439
- break;
440
- }
441
- const shouldforceQuineCheats = options.get(`force-quine-cheats`);
442
- if (shouldforceQuineCheats != undefined && typeof shouldforceQuineCheats != `boolean`) {
443
- logError(`The value for ${colourN(`--force-quine-cheats`)} must be ${colourV(`true`)} or ${colourV(`false`)}\n`);
444
- logHelp();
445
- break;
446
- }
447
- const {
448
- watch
449
- } = await watchModule;
450
- watch(sourcePath, hackmudPath, {
451
- scripts,
452
- onPush: info => logInfo(info, hackmudPath),
453
- typeDeclarationPath: (_ref = options.get(`type-declaration-path`) || options.get(`type-declaration`) || options.get(`dts`) || options.get(`gen-types`)) === null || _ref === void 0 ? void 0 : _ref.toString(),
454
- minify: shouldMinify,
455
- mangleNames: shouldMangleNames,
456
- onReady: () => log(`Watching`),
457
- forceQuineCheats: shouldforceQuineCheats
458
- });
459
- autoExit = false;
460
- }
461
- break;
462
- case `pull`:
463
- {
464
- const {
465
- hackmudPath
466
- } = await configPromise;
467
- if (!hackmudPath) {
468
- logNeedHackmudPathMessage();
469
- break;
470
- }
471
- const script = commands[1];
472
- if (!script) {
473
- logError(`Must provide the script to pull\n`);
474
- logHelp();
475
- break;
476
- }
477
- const sourcePath = commands[2] || `.`;
478
- try {
479
- await pull(sourcePath, hackmudPath, script);
480
- } catch (error) {
481
- console.error(error);
482
- logError(`Something went wrong, did you forget to ${colourC(`#down`)} the script?`);
483
- }
484
- }
485
- break;
486
- case `sync-macros`:
487
- {
488
- const {
489
- hackmudPath
490
- } = await configPromise;
491
- if (!hackmudPath) {
492
- logNeedHackmudPathMessage();
493
- break;
494
- }
495
- const {
496
- macrosSynced,
497
- usersSynced
498
- } = await syncMacros(hackmudPath);
499
- log(`Synced ${macrosSynced} macros to ${usersSynced} users`);
500
- }
501
- break;
502
- case `generate-type-declaration`:
503
- case `gen-type-declaration`:
504
- case `gen-dts`:
505
- case `gen-types`:
506
- {
507
- const target = commands[1];
508
- if (!target) {
509
- logError(`Must provide target directory\n`);
510
- logHelp();
511
- break;
512
- }
513
- const sourcePath = resolve(target);
514
- const outputPath = commands[2] || `./player.d.ts`;
515
- const typeDeclaration = await generateTypeDeclaration(sourcePath, (await configPromise).hackmudPath);
516
- let typeDeclarationPath = resolve(outputPath);
517
- try {
518
- await writeFile(typeDeclarationPath, typeDeclaration);
519
- } catch (error) {
520
- assert(error instanceof Error);
521
- if (!(error.code == `EISDIR`)) throw error;
522
- typeDeclarationPath = resolve(typeDeclarationPath, `player.d.ts`);
523
- await writeFile(typeDeclarationPath, typeDeclaration);
524
- }
525
- log(`Wrote type declaration to ${chalk.bold(typeDeclarationPath)}`);
526
- }
527
- break;
528
- case `config`:
529
- {
530
- switch (commands[1]) {
531
- case `get`:
532
- {
533
- const key = commands[2];
534
- if (key) log(exploreObject(await configPromise, key.split(`.`)));else console.log(await configPromise);
535
- }
536
- break;
537
- case `delete`:
538
- {
539
- var _exploreObject;
540
- const key = commands[2];
541
- if (!key) {
542
- logError(`Must provide a key to delete\n`);
543
- logHelp();
544
- break;
545
- }
546
- const keyParts = key.split(`.`);
547
- const pathName = keyParts.map(name => /^[A-Za-z_$][\w$]*$/.test(name) ? name : JSON.stringify(name)).join(`.`);
548
- const lastKey = keyParts.pop();
549
- const config = await configPromise;
550
- (_exploreObject = exploreObject(config, keyParts)) === null || _exploreObject === void 0 || delete _exploreObject[lastKey];
551
- log(`Removed ${colourV(pathName)} from config file`);
552
- }
553
- break;
554
- case `set`:
555
- {
556
- const key = commands[2];
557
- const value = commands[3];
558
- if (!key) {
559
- logError(`Must provide a key and value\n`);
560
- logHelp();
561
- break;
562
- }
563
- const keys = key.split(`.`);
564
- const pathName = keys.map(name => /^[A-Za-z_$][\w$]*$/.test(name) ? name : JSON.stringify(name)).join(`.`);
565
- if (!value) {
566
- logError(`Must provide a value for the key ${pathName}\n`);
567
- logHelp();
568
- break;
569
- }
570
- const lastKey = keys.pop();
571
- const config = await configPromise;
572
- if (!keys.length && lastKey == `hackmudPath`) config.hackmudPath = resolve(value.startsWith(`~/`) ? homedir() + value.slice(1) : value);else {
573
- let object = config;
574
- for (const key of keys) {
575
- if (typeof object[key] == `object`) object = object[key];else {
576
- object[key] = {};
577
- object = object[key];
578
- }
579
- }
580
- object[lastKey] = value;
581
- }
582
- console.log(config);
583
- await updateConfig(config);
584
- }
585
- break;
586
- default:
587
- {
588
- if (commands[1]) logError(`Unknown command: ${JSON.stringify(commands[1])}\n`);
589
- logHelp();
590
- }
591
- }
592
- }
593
- break;
594
- case `help`:
595
- case `h`:
596
- {
597
- logHelp();
598
- }
599
- break;
600
- case `golf`:
601
- case `minify`:
602
- {
603
- const target = commands[1];
604
- if (!target) {
605
- logError(`Must provide target\n`);
606
- logHelp();
607
- break;
608
- }
609
- const fileExtension = extname(target);
610
- if (!supportedExtensions.includes(fileExtension)) {
611
- logError(`Unsupported file extension "${chalk.bold(fileExtension)}"\nSupported extensions are "${supportedExtensions.map(extension => chalk.bold(extension)).join(`", "`)}"`);
612
- break;
613
- }
614
- const {
615
- processScript
616
- } = await processScriptModule;
617
- const fileBaseName = basename(target, fileExtension);
618
- // eslint-disable-next-line unicorn/prevent-abbreviations -- the file extension is `src` not `source`
619
- const fileBaseNameEndsWithDotSrc = fileBaseName.endsWith(`.src`);
620
- const scriptName = fileBaseNameEndsWithDotSrc ? fileBaseName.slice(0, -4) : fileBaseName;
621
- const scriptUser = basename(resolve(target, `..`)) == `scripts` && basename(resolve(target, `../../..`)) == `hackmud` ? basename(resolve(target, `../..`)) : `UNKNOWN`;
622
- const minify = !options.get(`skip-minify`);
623
- if (options.has(`skip-minify`) && options.has(`mangle-names`)) {
624
- logError(`Option ${colourN(`--mangle-names`)} would have no effect if minification is skipped\n`);
625
- logHelp();
626
- break;
627
- }
628
- const mangleNames_ = options.get(`mangle-names`);
629
- if (mangleNames_ != undefined && typeof mangleNames_ != `boolean`) {
630
- logError(`The value for ${colourN(`--mangle-names`)} must be ${colourV(`true`)} or ${colourV(`false`)}\n`);
631
- logHelp();
632
- break;
633
- }
634
- const mangleNames = mangleNames_;
635
- const forceQuineCheats_ = options.get(`force-quine-cheats`);
636
- if (forceQuineCheats_ != undefined && typeof forceQuineCheats_ != `boolean`) {
637
- logError(`the value for ${colourN(`--force-quine-cheats`)} must be ${colourV(`true`)} or ${colourV(`false`)}\n`);
638
- logHelp();
639
- break;
640
- }
641
- const forceQuineCheats = forceQuineCheats_;
642
- let outputPath = commands[2] || resolve(dirname(target), fileBaseNameEndsWithDotSrc ? `${scriptName}.js` : fileExtension == `.js` ? `${fileBaseName}.min.js` : `${fileBaseName}.js`);
643
- const golfFile = () => readFile(target, {
644
- encoding: `utf-8`
645
- }).then(async source => {
646
- const timeStart = performance.now();
647
- const {
648
- script,
649
- warnings
650
- } = await processScript(source, {
651
- minify,
652
- scriptUser,
653
- scriptName,
654
- filePath: target,
655
- mangleNames,
656
- forceQuineCheats
657
- });
658
- const timeTook = performance.now() - timeStart;
659
- for (const {
660
- message,
661
- line
662
- } of warnings) log(`Warning "${chalk.bold(message)}" on line ${chalk.bold(String(line))}`);
663
- await writeFilePersistent(outputPath, script).catch(async error => {
664
- if (!commands[2] || error.code != `EISDIR`) throw error;
665
- outputPath = resolve(outputPath, `${basename(target, fileExtension)}.js`);
666
- await writeFilePersistent(outputPath, script);
667
- }).then(() => log(`Wrote ${chalk.bold(countHackmudCharacters(script))} chars to ${chalk.bold(relative(`.`, outputPath))} | took ${Math.round(timeTook * 100) / 100}ms`), error => logError(error.message));
668
- }, error => logError(error.message));
669
- if (options.get(`watch`)) {
670
- const {
671
- watch: watchFile
672
- } = await chokidarModule;
673
- watchFile(target, {
674
- awaitWriteFinish: {
675
- stabilityThreshold: 100
676
- }
677
- }).on(`ready`, () => log(`Watching ${target}`)).on(`change`, golfFile);
678
- autoExit = false;
679
- } else await golfFile();
680
- }
681
- break;
682
- default:
683
- {
684
- if (commands[0]) logError(`Unknown command: ${JSON.stringify(commands[0])}\n`);
685
- logHelp();
686
- }
687
- }
688
- if (autoExit) process.exit();
2
+ import{DynamicMap as e}from"@samual/lib/DynamicMap";import{assert as t}from"@samual/lib/assert";import{countHackmudCharacters as n}from"@samual/lib/countHackmudCharacters";import{writeFilePersistent as a}from"@samual/lib/writeFilePersistent";import{readFile as s,writeFile as o,mkdir as i,rmdir as r}from"fs/promises";import{homedir as c}from"os";import{supportedExtensions as l}from"../constants.js";import{generateTypeDeclaration as f}from"../generateTypeDeclaration.js";import{pull as $}from"../pull.js";import{syncMacros as p}from"../syncMacros.js";import{resolve as m,extname as h,basename as u,dirname as g,relative as d}from"path";import"@samual/lib/copyFilePersistent";const y="0.19.0-fa82f73",b=m(c(),".config"),k=m(b,"hsm.json"),w=new Map,v=[],S=new e((e=>{let t=0;for(const n of e)t+=(t>>1)+t+"xi1_8ratvsw9hlbgm02y5zpdcn7uekof463qj".indexOf(n)+1;return[_,x,J,E,I,z][t%6](e)})),logNeedHackmudPathMessage=()=>console.error(A(`${W("You need to set hackmudPath in config before you can use this command")}\n\n${q("To fix this:")}\nOpen hackmud and run "${C("#dir")}"\nThis will open a file browser and print your hackmud user's script directory\nGo up 2 directories and then copy the path\nThen in a terminal run "${C("hsm")} ${I("config set")} ${D("hackmudPath")} ${z("<the path you copied>")}"`)),logHelp=()=>{const e="Push scripts from a directory to hackmud user's scripts directories",t="Watch a directory and push a script when modified",n="Minify a script file on the spot",a="Generate a type declaration file for a directory of scripts",s="Sync macros across all hackmud users",o="Retrieve a value from the config file",i="Assign a value to the config file",r="Remove a key and value from the config file",c="Pull a script a from a hackmud user's script directory",l="Skip minification to produce a readable script",f="Reduce character count further but lose function names in error call stacks",$="Force quine cheats even if the character count is higher";switch(console.log(R("Version")+A(": ")+D(y)),v[0]){case"config":switch(v[1]){case"get":console.log(`\n${_(o)}\n\n${q("Usage:")}\n${C("hsm")} ${I(`${v[0]} ${v[1]}`)} ${z("<key>")}`);break;case"set":console.log(`\n${_(i)}\n\n${q("Usage:")}\n${C("hsm")} ${I(`${v[0]} ${v[1]}`)} ${z("<key> <value>")}`);break;case"delete":console.log(`\n${_(r)}\n\n${q("Usage:")}\n${C("hsm")} ${I(`${v[0]} ${v[1]}`)} ${z("<key>")}`);break;default:console.log(A(`${R("Config path")}: ${D(k)}\n\n${_("Modify the config file")}\n\n${q("Usage:")}\n${C("hsm")} ${I(`${v[0]} get`)} ${z("<key>")}\n ${o}\n${C("hsm")} ${I(`${v[0]} set`)} ${z("<key> <value>")}\n ${i}\n${C("hsm")} ${I(`${v[0]} delete`)} ${z("<key>")}\n ${r}`))}break;case"push":console.log(A(`\n${_(e)}\n\n${q("Usage:")}\n${C("hsm")} ${I(v[0])} ${z("<directory> [<script user>.<script name>...]")}\n\n${q("Options:")}\n${R("--skip-minify")}\n ${l}\n${R("--mangle-names")}\n ${f}\n${R("--force-quine-cheats")}\n ${$}`));break;case"dev":case"watch":console.log(A(`${R("Aliases")}: ${D("watch, dev")}\n\n${_(t)}\n\n${q("Usage:")}\n${C("hsm")} ${I(v[0])} ${z("<directory> [<script user>.<script name>...]")}\n\n${q("Options:")}\n${R("--skip-minify")}\n ${l}\n${R("--mangle-names")}\n ${f}\n${R("--type-declaration-path")}=${z("<path>")}\n Path to generate a type declaration file for the scripts\n${R("--force-quine-cheats")}\n ${$}`));break;case"pull":console.log(A(`\n${_(c)}\n\n${q("Usage:")}\n${C("hsm")} ${I(v[0])} ${z("<script user>")}${D(".")}${z("<script name>")}`));break;case"minify":case"golf":console.log(A(`${R("Aliases")}: ${D("minify, golf")}\n\n${_(n)}\n\n${q("Usage:")}\n${C("hsm")} ${I(v[0])} ${z("<target> [output path]")}\n\n${q("Options:")}\n${R("--skip-minify")}\n ${l}\n${R("--mangle-names")}\n ${f}\n${R("--force-quine-cheats")}\n ${$}\n${R("--watch")}\n Watch for changes`));break;case"generate-type-declaration":case"gen-type-declaration":case"gen-dts":case"gen-types":console.log(A(`${R("Aliases")}: ${D("generate-type-declaration, gen-type-declaration, gen-types, gen-dts")}\n\n${_(a)}\n\n${q("Usage:")}\n${C("hsm")} ${I(v[0])} ${z("<directory> [output path]")}`));break;case"sync-macros":console.log(`\n${_(s)}`);break;default:console.log(A(`\n${_("Hackmud Script Manager")}\n\n${q("Commands:")}\n${I("push")}\n ${e}\n${I("watch")}, ${I("dev")}\n ${t}\n${I("minify")}, ${I("golf")}\n ${n}\n${I("generate-type-declaration")}, ${I("gen-type-declaration")}, ${I("gen-types")}, ${I("gen-dts")}\n ${a}\n${I("sync-macros")}\n ${s}\n${I("config")}\n Modify and view the config file\n${I("pull")}\n ${c}`))}},exploreObject=(e,t,n=!1)=>{for(const a of t)e=n?"object"==typeof e[a]?e[a]:e[a]={}:e?.[a];return e},logInfo=({file:e,users:t,minLength:n,error:a},s)=>{a?logError(`error "${T.bold(a.message)}" in ${T.bold(e)}`):console.log(`pushed ${T.bold(e)} to ${t.map((e=>T.bold(S.get(e)))).join(", ")} | ${T.bold(String(n))} chars | ${T.bold(`${m(s,t[0],"scripts",u(e,h(e)))}.js`)}`)},log=e=>{console.log(A(e))},logError=e=>{console.error(W(e)),process.exitCode=1};for(const e of process.argv.slice(2))if("-"==e[0]){const[t,n]=e.split("=");let a=n;if(a)if("true"==a)a=!0;else if("false"==a)a=!1;else{const e=Number(a);isFinite(e)&&(a=e)}else a=!0;if("-"==e[1])w.set(t.slice(2),a);else for(const e of t.slice(1))w.set(e,a)}else v.push(e);("v"==v[0]||"version"==v[0]||w.get("version")||w.get("v"))&&(console.log(y),process.exit());let P=!1;const j=s(k,{encoding:"utf-8"}).then((e=>{let t;try{t=JSON.parse(e)}catch{return log("Config file was corrupted, resetting"),{}}return t&&"object"==typeof t?("hackmudPath"in t&&"string"!=typeof t.hackmudPath&&(log('Property "hackmudPath" of config file was corrupted, removing'),delete t.hackmudPath),t):(log("Config file was corrupted, resetting"),{})}),(()=>(P=!0,{}))),N=import("../push.js"),O=import("../processScript/index.js"),M=import("../watch.js"),U=import("chokidar"),{default:T}=await import("chalk"),q=T.rgb(255,255,255),z=T.rgb(202,202,202),C=T.rgb(155,155,155),W=T.rgb(255,0,0),_=T.rgb(255,244,4),x=T.rgb(243,249,152),I=T.rgb(30,255,0),J=T.rgb(179,255,155),R=T.rgb(0,255,255),A=T.rgb(122,178,244),D=T.rgb(255,0,236),E=T.rgb(255,150,224);(w.get("help")||w.get("h"))&&(logHelp(),process.exit());let F=!0;switch(v[0]){case"push":{const{hackmudPath:e}=await j;if(!e){logNeedHackmudPathMessage();break}const t=v[1];if(!t){logError("Must provide the directory to push from\n"),logHelp();break}const n=v.slice(2);if(n.length){const e=n.find((e=>!/^(?:[a-z_][a-z\d_]{0,24}|\*)\.(?:[a-z_][a-z\d_]{0,24}|\*)$/.test(e)));if(e){logError(`Invalid script name: ${JSON.stringify(e)}\n`),logHelp();break}}else n.push("*.*");if(w.has("skip-minify")&&w.has("mangle-names")){logError(`Option ${R("--mangle-names")} is not compatible with ${R("--skip-minify")}\n`),logHelp();break}const a=w.get("skip-minify");let s;if(null!=a){if("boolean"!=typeof a){logError(`The value for ${R("--skip-minify")} must be ${D("true")} or ${D("false")}\n`),logHelp();break}s=!a}const o=w.get("mangle-names");if(null!=o&&"boolean"!=typeof o){logError(`The value for ${R("--mangle-names")} must be ${D("true")} or ${D("false")}\n`),logHelp();break}const i=w.get("force-quine-cheats");if(null!=i&&"boolean"!=typeof i){logError(`The value for ${R("--force-quine-cheats")} must be ${D("true")} or ${D("false")}\n`),logHelp();break}const{push:r}=await N;(await r(t,e,{scripts:n,onPush:t=>logInfo(t,e),minify:s,mangleNames:o,forceQuineCheats:i})).length||logError("Could not find any scripts to push")}break;case"dev":case"watch":{const{hackmudPath:e}=await j;if(!e){logNeedHackmudPathMessage();break}const t=v[1];if(!t){logError("Must provide the directory to watch\n"),logHelp();break}const n=v.slice(2);if(n.length){const e=n.find((e=>!/^(?:[a-z_][a-z\d_]{0,24}|\*)\.(?:[a-z_][a-z\d_]{0,24}|\*)$/.test(e)));if(e){logError(`Invalid script name: ${JSON.stringify(e)}\n`),logHelp();break}}else n.push("*.*");if(w.has("skip-minify")&&w.has("mangle-names")){logError(`Option ${R("--mangle-names")} is not compatible with ${R("--skip-minify")}\n`),logHelp();break}const a=w.get("skip-minify");let s;if(null!=a){if("boolean"!=typeof a){logError(`The value for ${R("--skip-minify")} must be ${D("true")} or ${D("false")}\n`),logHelp();break}s=!a}const o=w.get("mangle-names");if(null!=o&&"boolean"!=typeof o){logError(`The value for ${R("--mangle-names")} must be ${D("true")} or ${D("false")}\n`),logHelp();break}const i=w.get("force-quine-cheats");if(null!=i&&"boolean"!=typeof i){logError(`The value for ${R("--force-quine-cheats")} must be ${D("true")} or ${D("false")}\n`),logHelp();break}const{watch:r}=await M;r(t,e,{scripts:n,onPush:t=>logInfo(t,e),typeDeclarationPath:(w.get("type-declaration-path")||w.get("type-declaration")||w.get("dts")||w.get("gen-types"))?.toString(),minify:s,mangleNames:o,onReady:()=>log("Watching"),forceQuineCheats:i}),F=!1}break;case"pull":{const{hackmudPath:e}=await j;if(!e){logNeedHackmudPathMessage();break}const t=v[1];if(!t){logError("Must provide the script to pull\n"),logHelp();break}const n=v[2]||".";try{await $(n,e,t)}catch(e){console.error(e),logError(`Something went wrong, did you forget to ${C("#down")} the script?`)}}break;case"sync-macros":{const{hackmudPath:e}=await j;if(!e){logNeedHackmudPathMessage();break}const{macrosSynced:t,usersSynced:n}=await p(e);log(`Synced ${t} macros to ${n} users`)}break;case"generate-type-declaration":case"gen-type-declaration":case"gen-dts":case"gen-types":{const e=v[1];if(!e){logError("Must provide target directory\n"),logHelp();break}const n=m(e),a=v[2]||"./player.d.ts",s=await f(n,(await j).hackmudPath);let i=m(a);try{await o(i,s)}catch(e){if(t(e instanceof Error),"EISDIR"!=e.code)throw e;i=m(i,"player.d.ts"),await o(i,s)}log(`Wrote type declaration to ${T.bold(i)}`)}break;case"config":switch(v[1]){case"get":{const e=v[2];e?log(exploreObject(await j,e.split("."))):console.log(await j)}break;case"delete":{const e=v[2];if(!e){logError("Must provide a key to delete\n"),logHelp();break}const t=e.split("."),n=t.map((e=>/^[A-Za-z_$][\w$]*$/.test(e)?e:JSON.stringify(e))).join("."),a=t.pop(),s=await j;delete exploreObject(s,t)?.[a],log(`Removed ${D(n)} from config file`)}break;case"set":{const e=v[2],t=v[3];if(!e){logError("Must provide a key and value\n"),logHelp();break}const n=e.split("."),a=n.map((e=>/^[A-Za-z_$][\w$]*$/.test(e)?e:JSON.stringify(e))).join(".");if(!t){logError(`Must provide a value for the key ${a}\n`),logHelp();break}const s=n.pop(),l=await j;if(n.length||"hackmudPath"!=s){let e=l;for(const t of n)"object"==typeof e[t]||(e[t]={}),e=e[t];e[s]=t}else l.hackmudPath=m(t.startsWith("~/")?c()+t.slice(1):t);console.log(l),await(async e=>{const t=JSON.stringify(e,void 0,"\t");P&&log(`Creating config file at ${k}`),await o(k,t).catch((async e=>{switch(e.code){case"EISDIR":await r(k);break;case"ENOENT":await i(b);break;default:throw e}await o(k,t)}))})(l)}break;default:v[1]&&logError(`Unknown command: ${JSON.stringify(v[1])}\n`),logHelp()}break;case"help":case"h":logHelp();break;case"golf":case"minify":{const e=v[1];if(!e){logError("Must provide target\n"),logHelp();break}const t=h(e);if(!l.includes(t)){logError(`Unsupported file extension "${T.bold(t)}"\nSupported extensions are "${l.map((e=>T.bold(e))).join('", "')}"`);break}const{processScript:o}=await O,i=u(e,t),r=i.endsWith(".src"),c=r?i.slice(0,-4):i,f="scripts"==u(m(e,".."))&&"hackmud"==u(m(e,"../../.."))?u(m(e,"../..")):"UNKNOWN",$=!w.get("skip-minify");if(w.has("skip-minify")&&w.has("mangle-names")){logError(`Option ${R("--mangle-names")} would have no effect if minification is skipped\n`),logHelp();break}const p=w.get("mangle-names");if(null!=p&&"boolean"!=typeof p){logError(`The value for ${R("--mangle-names")} must be ${D("true")} or ${D("false")}\n`),logHelp();break}const y=p,b=w.get("force-quine-cheats");if(null!=b&&"boolean"!=typeof b){logError(`the value for ${R("--force-quine-cheats")} must be ${D("true")} or ${D("false")}\n`),logHelp();break}const k=b;let S=v[2]||m(g(e),r?`${c}.js`:".js"==t?`${i}.min.js`:`${i}.js`);const golfFile=()=>s(e,{encoding:"utf-8"}).then((async s=>{const i=performance.now(),{script:r,warnings:l}=await o(s,{minify:$,scriptUser:f,scriptName:c,filePath:e,mangleNames:y,forceQuineCheats:k}),p=performance.now()-i;for(const{message:e,line:t}of l)log(`Warning "${T.bold(e)}" on line ${T.bold(String(t))}`);await a(S,r).catch((async n=>{if(!v[2]||"EISDIR"!=n.code)throw n;S=m(S,`${u(e,t)}.js`),await a(S,r)})).then((()=>log(`Wrote ${T.bold(n(r))} chars to ${T.bold(d(".",S))} | took ${Math.round(100*p)/100}ms`)),(e=>logError(e.message)))}),(e=>logError(e.message)));if(w.get("watch")){const{watch:t}=await U;t(e,{awaitWriteFinish:{stabilityThreshold:100}}).on("ready",(()=>log(`Watching ${e}`))).on("change",golfFile),F=!1}else await golfFile()}break;default:v[0]&&logError(`Unknown command: ${JSON.stringify(v[0])}\n`),logHelp()}F&&process.exit();