hackmud-script-manager 0.19.0-50a29ed → 0.19.0-c12fd7c

Sign up to get free protection for your applications and to get access to all the features.
package/bin/hsm.js CHANGED
@@ -1,2 +1,688 @@
1
1
  #!/usr/bin/env node
2
- import{readFile as e,writeFile as t,mkdir as n,rmdir as a}from"fs/promises";import{homedir as s}from"os";import{s as o}from"../constants-9bb78688.js";import{generateTypeDeclaration as i}from"../generateTypeDeclaration.js";import{pull as r}from"../pull.js";import{syncMacros as c}from"../syncMacros.js";import{resolve as l,extname as f,basename as $,dirname as p,relative as h}from"path";import{D as m,w as d}from"../writeFilePersistent-ee9c9bfd.js";import{a as g}from"../assert-22a7ef8a.js";import{c as u}from"../countHackmudCharacters-a08a265f.js";import"fs";const y=l(s(),".config"),b=l(y,"hsm.json"),k=new Map,w=[],v=new m((e=>{let t=0;for(const n of e)t+=(t>>1)+t+"xi1_8ratvsw9hlbgm02y5zpdcn7uekof463qj".indexOf(n)+1;return[W,_,I,D,x,q][t%6](e)})),logNeedHackmudPathMessage=()=>console.error(R(`${C("You need to set hackmudPath in config before you can use this command")}\n\n${T("To fix this:")}\nOpen hackmud and run "${z("#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 "${z("hsm")} ${x("config set")} ${A("hackmudPath")} ${q("<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(J("Version")+R(": ")+A("0.19.0-50a29ed")),w[0]){case"config":switch(w[1]){case"get":console.log(`\n${W(o)}\n\n${T("Usage:")}\n${z("hsm")} ${x(`${w[0]} ${w[1]}`)} ${q("<key>")}`);break;case"set":console.log(`\n${W(i)}\n\n${T("Usage:")}\n${z("hsm")} ${x(`${w[0]} ${w[1]}`)} ${q("<key> <value>")}`);break;case"delete":console.log(`\n${W(r)}\n\n${T("Usage:")}\n${z("hsm")} ${x(`${w[0]} ${w[1]}`)} ${q("<key>")}`);break;default:console.log(R(`${J("Config path")}: ${A(b)}\n\n${W("Modify the config file")}\n\n${T("Usage:")}\n${z("hsm")} ${x(`${w[0]} get`)} ${q("<key>")}\n ${o}\n${z("hsm")} ${x(`${w[0]} set`)} ${q("<key> <value>")}\n ${i}\n${z("hsm")} ${x(`${w[0]} delete`)} ${q("<key>")}\n ${r}`))}break;case"push":console.log(R(`\n${W(e)}\n\n${T("Usage:")}\n${z("hsm")} ${x(w[0])} ${q("<directory> [<script user>.<script name>...]")}\n\n${T("Options:")}\n${J("--skip-minify")}\n ${l}\n${J("--mangle-names")}\n ${f}\n${J("--force-quine-cheats")}\n ${$}`));break;case"dev":case"watch":console.log(R(`${J("Aliases")}: ${A("watch, dev")}\n\n${W(t)}\n\n${T("Usage:")}\n${z("hsm")} ${x(w[0])} ${q("<directory> [<script user>.<script name>...]")}\n\n${T("Options:")}\n${J("--skip-minify")}\n ${l}\n${J("--mangle-names")}\n ${f}\n${J("--type-declaration-path")}=${q("<path>")}\n Path to generate a type declaration file for the scripts\n${J("--force-quine-cheats")}\n ${$}`));break;case"pull":console.log(R(`\n${W(c)}\n\n${T("Usage:")}\n${z("hsm")} ${x(w[0])} ${q("<script user>")}${A(".")}${q("<script name>")}`));break;case"minify":case"golf":console.log(R(`${J("Aliases")}: ${A("minify, golf")}\n\n${W(n)}\n\n${T("Usage:")}\n${z("hsm")} ${x(w[0])} ${q("<target> [output path]")}\n\n${T("Options:")}\n${J("--skip-minify")}\n ${l}\n${J("--mangle-names")}\n ${f}\n${J("--force-quine-cheats")}\n ${$}\n${J("--watch")}\n Watch for changes`));break;case"generate-type-declaration":case"gen-type-declaration":case"gen-dts":case"gen-types":console.log(R(`${J("Aliases")}: ${A("generate-type-declaration, gen-type-declaration, gen-types, gen-dts")}\n\n${W(a)}\n\n${T("Usage:")}\n${z("hsm")} ${x(w[0])} ${q("<directory> [output path]")}`));break;case"sync-macros":console.log(`\n${W(s)}`);break;default:console.log(R(`\n${W("Hackmud Script Manager")}\n\n${T("Commands:")}\n${x("push")}\n ${e}\n${x("watch")}, ${x("dev")}\n ${t}\n${x("minify")}, ${x("golf")}\n ${n}\n${x("generate-type-declaration")}, ${x("gen-type-declaration")}, ${x("gen-types")}, ${x("gen-dts")}\n ${a}\n${x("sync-macros")}\n ${s}\n${x("config")}\n Modify and view the config file\n${x("pull")}\n ${c}`))}},exploreObject=(e,t,n=!1)=>{for(const s of t){var a;e=n?"object"==typeof e[s]?e[s]:e[s]={}:null===(a=e)||void 0===a?void 0:a[s]}return e},logInfo=({file:e,users:t,minLength:n,error:a},s)=>{a?logError(`error "${U.bold(a.message)}" in ${U.bold(e)}`):console.log(`pushed ${U.bold(e)} to ${t.map((e=>U.bold(v.get(e)))).join(", ")} | ${U.bold(String(n))} chars | ${U.bold(`${l(s,t[0],"scripts",$(e,f(e)))}.js`)}`)},log=e=>{console.log(R(e))},logError=e=>{console.error(C(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])k.set(t.slice(2),a);else for(const e of t.slice(1))k.set(e,a)}else w.push(e);("v"==w[0]||"version"==w[0]||k.get("version")||k.get("v"))&&(console.log("0.19.0-50a29ed"),process.exit());let j=!1;const S=e(b,{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"),{})}),(()=>(j=!0,{}))),P=import("../push.js"),N=import("../processScript/index.js"),O=import("../watch.js"),M=import("chokidar"),{default:U}=await import("chalk"),T=U.rgb(255,255,255),q=U.rgb(202,202,202),z=U.rgb(155,155,155),C=U.rgb(255,0,0),W=U.rgb(255,244,4),_=U.rgb(243,249,152),x=U.rgb(30,255,0),I=U.rgb(179,255,155),J=U.rgb(0,255,255),R=U.rgb(122,178,244),A=U.rgb(255,0,236),D=U.rgb(255,150,224);(k.get("help")||k.get("h"))&&(logHelp(),process.exit());let E=!0;switch(w[0]){case"push":{const{hackmudPath:e}=await S;if(!e){logNeedHackmudPathMessage();break}const t=w[1];if(!t){logError("Must provide the directory to push from\n"),logHelp();break}const n=w.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(k.has("skip-minify")&&k.has("mangle-names")){logError(`Option ${J("--mangle-names")} is not compatible with ${J("--skip-minify")}\n`),logHelp();break}const a=k.get("skip-minify");let s;if(null!=a){if("boolean"!=typeof a){logError(`The value for ${J("--skip-minify")} must be ${A("true")} or ${A("false")}\n`),logHelp();break}s=!a}const o=k.get("mangle-names");if(null!=o&&"boolean"!=typeof o){logError(`The value for ${J("--mangle-names")} must be ${A("true")} or ${A("false")}\n`),logHelp();break}const i=k.get("force-quine-cheats");if(null!=i&&"boolean"!=typeof i){logError(`The value for ${J("--force-quine-cheats")} must be ${A("true")} or ${A("false")}\n`),logHelp();break}const{push:r}=await P;(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":{var F;const{hackmudPath:e}=await S;if(!e){logNeedHackmudPathMessage();break}const t=w[1];if(!t){logError("Must provide the directory to watch\n"),logHelp();break}const n=w.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(k.has("skip-minify")&&k.has("mangle-names")){logError(`Option ${J("--mangle-names")} is not compatible with ${J("--skip-minify")}\n`),logHelp();break}const a=k.get("skip-minify");let s;if(null!=a){if("boolean"!=typeof a){logError(`The value for ${J("--skip-minify")} must be ${A("true")} or ${A("false")}\n`),logHelp();break}s=!a}const o=k.get("mangle-names");if(null!=o&&"boolean"!=typeof o){logError(`The value for ${J("--mangle-names")} must be ${A("true")} or ${A("false")}\n`),logHelp();break}const i=k.get("force-quine-cheats");if(null!=i&&"boolean"!=typeof i){logError(`The value for ${J("--force-quine-cheats")} must be ${A("true")} or ${A("false")}\n`),logHelp();break}const{watch:r}=await O;r(t,e,{scripts:n,onPush:t=>logInfo(t,e),typeDeclarationPath:null===(F=k.get("type-declaration-path")||k.get("type-declaration")||k.get("dts")||k.get("gen-types"))||void 0===F?void 0:F.toString(),minify:s,mangleNames:o,onReady:()=>log("Watching"),forceQuineCheats:i}),E=!1}break;case"pull":{const{hackmudPath:e}=await S;if(!e){logNeedHackmudPathMessage();break}const t=w[1];if(!t){logError("Must provide the script to pull\n"),logHelp();break}const n=w[2]||".";try{await r(n,e,t)}catch(e){console.error(e),logError(`Something went wrong, did you forget to ${z("#down")} the script?`)}}break;case"sync-macros":{const{hackmudPath:e}=await S;if(!e){logNeedHackmudPathMessage();break}const{macrosSynced:t,usersSynced:n}=await c(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=w[1];if(!e){logError("Must provide target directory\n"),logHelp();break}const n=l(e),a=w[2]||"./player.d.ts",s=await i(n,(await S).hackmudPath);let o=l(a);try{await t(o,s)}catch(e){if(g(e instanceof Error),"EISDIR"!=e.code)throw e;o=l(o,"player.d.ts"),await t(o,s)}log(`Wrote type declaration to ${U.bold(o)}`)}break;case"config":switch(w[1]){case"get":{const e=w[2];e?log(exploreObject(await S,e.split("."))):console.log(await S)}break;case"delete":{var Q;const e=w[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();null===(Q=exploreObject(await S,t))||void 0===Q||delete Q[a],log(`Removed ${A(n)} from config file`)}break;case"set":{const e=w[2],o=w[3];if(!e){logError("Must provide a key and value\n"),logHelp();break}const i=e.split("."),r=i.map((e=>/^[A-Za-z_$][\w$]*$/.test(e)?e:JSON.stringify(e))).join(".");if(!o){logError(`Must provide a value for the key ${r}\n`),logHelp();break}const c=i.pop(),f=await S;if(i.length||"hackmudPath"!=c){let e=f;for(const t of i)"object"==typeof e[t]||(e[t]={}),e=e[t];e[c]=o}else f.hackmudPath=l(o.startsWith("~/")?s()+o.slice(1):o);console.log(f),await(async e=>{const s=JSON.stringify(e,void 0,"\t");j&&log(`Creating config file at ${b}`),await t(b,s).catch((async e=>{switch(e.code){case"EISDIR":await a(b);break;case"ENOENT":await n(y);break;default:throw e}await t(b,s)}))})(f)}break;default:w[1]&&logError(`Unknown command: ${JSON.stringify(w[1])}\n`),logHelp()}break;case"help":case"h":logHelp();break;case"golf":case"minify":{const t=w[1];if(!t){logError("Must provide target\n"),logHelp();break}const n=f(t);if(!o.includes(n)){logError(`Unsupported file extension "${U.bold(n)}"\nSupported extensions are "${o.map((e=>U.bold(e))).join('", "')}"`);break}const{processScript:a}=await N,s=$(t,n),i=s.endsWith(".src"),r=i?s.slice(0,-4):s,c="scripts"==$(l(t,".."))&&"hackmud"==$(l(t,"../../.."))?$(l(t,"../..")):"UNKNOWN",m=!k.get("skip-minify");if(k.has("skip-minify")&&k.has("mangle-names")){logError(`Option ${J("--mangle-names")} would have no effect if minification is skipped\n`),logHelp();break}const g=k.get("mangle-names");if(null!=g&&"boolean"!=typeof g){logError(`The value for ${J("--mangle-names")} must be ${A("true")} or ${A("false")}\n`),logHelp();break}const y=g,b=k.get("force-quine-cheats");if(null!=b&&"boolean"!=typeof b){logError(`the value for ${J("--force-quine-cheats")} must be ${A("true")} or ${A("false")}\n`),logHelp();break}const v=b;let j=w[2]||l(p(t),i?`${r}.js`:".js"==n?`${s}.min.js`:`${s}.js`);const golfFile=()=>e(t,{encoding:"utf-8"}).then((async e=>{const s=performance.now(),{script:o,warnings:i}=await a(e,{minify:m,scriptUser:c,scriptName:r,filePath:t,mangleNames:y,forceQuineCheats:v}),f=performance.now()-s;for(const{message:e,line:t}of i)log(`Warning "${U.bold(e)}" on line ${U.bold(String(t))}`);await d(j,o).catch((async e=>{if(!w[2]||"EISDIR"!=e.code)throw e;j=l(j,`${$(t,n)}.js`),await d(j,o)})).then((()=>log(`Wrote ${U.bold(u(o))} chars to ${U.bold(h(".",j))} | took ${Math.round(100*f)/100}ms`)),(e=>logError(e.message)))}),(e=>logError(e.message)));if(k.get("watch")){const{watch:e}=await M;e(t,{awaitWriteFinish:{stabilityThreshold:100}}).on("ready",(()=>log(`Watching ${t}`))).on("change",golfFile),E=!1}else await golfFile()}break;default:w[0]&&logError(`Unknown command: ${JSON.stringify(w[0])}\n`),logHelp()}E&&process.exit();
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-c12fd7c";
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();