UNPKG

hackmud-script-manager

Version:

Script manager for game hackmud, with minification, TypeScript support, and player script type definition generation.

165 lines (164 loc) 6.88 kB
import { AutoMap } from "@samual/lib/AutoMap" import { ensure, assert } from "@samual/lib/assert" import { countHackmudCharacters } from "@samual/lib/countHackmudCharacters" import { readDirectoryWithStats } from "@samual/lib/readDirectoryWithStats" import { writeFilePersistent } from "@samual/lib/writeFilePersistent" import { readFile } from "fs/promises" import { basename, resolve } from "path" import { processScript } from "./processScript/index.js" import "@babel/generator" import "@babel/parser" import "@babel/plugin-proposal-decorators" import "@babel/plugin-proposal-destructuring-private" import "@babel/plugin-proposal-explicit-resource-management" import "@babel/plugin-transform-class-properties" import "@babel/plugin-transform-class-static-block" import "@babel/plugin-transform-exponentiation-operator" import "@babel/plugin-transform-json-strings" import "@babel/plugin-transform-logical-assignment-operators" import "@babel/plugin-transform-nullish-coalescing-operator" import "@babel/plugin-transform-numeric-separator" import "@babel/plugin-transform-object-rest-spread" import "@babel/plugin-transform-optional-catch-binding" import "@babel/plugin-transform-optional-chaining" import "@babel/plugin-transform-private-property-in-object" import "@babel/plugin-transform-unicode-sets-regex" import "@babel/traverse" import "@babel/types" import "@rollup/plugin-alias" import "@rollup/plugin-babel" import "@rollup/plugin-commonjs" import "@rollup/plugin-json" import "@rollup/plugin-node-resolve" import "prettier" import "rollup" import "./constants.js" import "./processScript/minify.js" import "@samual/lib/spliceString" import "acorn" import "terser" import "./processScript/shared.js" import "./processScript/postprocess.js" import "./processScript/preprocess.js" import "import-meta-resolve" import "./processScript/transform.js" import "@samual/lib/clearObject" class MissingSourceFolderError extends Error {} Object.defineProperty(MissingSourceFolderError.prototype, "name", { value: "MissingSourceFolderError" }) class MissingHackmudFolderError extends Error {} Object.defineProperty(MissingHackmudFolderError.prototype, "name", { value: "MissingHackmudFolderError" }) class NoUsersError extends Error {} Object.defineProperty(NoUsersError.prototype, "name", { value: "NoUsersError" }) class NoScriptsError extends Error {} Object.defineProperty(NoScriptsError.prototype, "name", { value: "NoScriptsError" }) async function push( sourcePath, hackmudPath, { scripts = ["*.*"], onPush = () => {}, minify = !0, mangleNames = !1, forceQuineCheats, rootFolderPath } = {} ) { const [sourceFolder, hackmudFolder] = await Promise.all([ readDirectoryWithStats(sourcePath).catch(error => { if (error && "ENOENT" == error.code) return new MissingSourceFolderError("There is no folder at " + sourcePath) throw error }), readDirectoryWithStats(hackmudPath).catch(error => { if (error && "ENOENT" == error.code) return new MissingHackmudFolderError("There is no folder at " + hackmudPath) throw error }) ]) if (sourceFolder instanceof Error) return sourceFolder if (hackmudFolder instanceof Error) return hackmudFolder const sourceFolderFolders = sourceFolder.filter( ({ name, stats }) => stats.isDirectory() && /^[a-z_][a-z\d_]{0,24}$/.test(name) ), allUsers = new Set([ ...scripts .map(scriptName => ensure(scriptName.split(".")[0], "src/push.ts:85:65")) .filter(name => "*" != name), ...sourceFolderFolders.map(({ name }) => name), ...hackmudFolder.filter(({ stats }) => stats.isDirectory()).map(({ name }) => name), ...hackmudFolder .filter(({ stats, name }) => stats.isFile() && name.endsWith(".key")) .map(({ name }) => name.slice(0, -4)) ]) if (!allUsers.size) return new NoUsersError( "Could not find any users. Either provide the names of your users or log into a user in hackmud" ) const usersToScriptsToPush = new AutoMap(_user => new Map()), scriptNamesToUsers = new AutoMap(_scriptName => new Set()) for (const script of scripts) { const [user, scriptName] = script.split(".") assert(user, "src/push.ts:108:16") assert(scriptName, "src/push.ts:109:22") "*" == user ? scriptNamesToUsers.set(scriptName, allUsers) : scriptNamesToUsers.get(scriptName).add(user) } const sourceFolderFiles = sourceFolder.filter(({ stats }) => stats.isFile()), wildScriptUsers_ = scriptNamesToUsers.get("*") scriptNamesToUsers.delete("*") for (const { name, path } of [ ...sourceFolderFiles.filter(({ name }) => name.endsWith(".js")), ...sourceFolderFiles.filter(({ name }) => name.endsWith(".ts")) ]) { const scriptName = name.slice(0, -3) for (const user of [...wildScriptUsers_, ...scriptNamesToUsers.get(scriptName)]) usersToScriptsToPush.get(user).set(scriptName, path) } await Promise.all( sourceFolderFolders.map(async ({ name: user, path }) => { const files = (await readDirectoryWithStats(path)).filter(({ stats }) => stats.isFile()), scriptFiles = [ ...files.filter(({ name }) => name.endsWith(".js")), ...files.filter(({ name }) => name.endsWith(".ts")) ] for (const { name, path } of scriptFiles) { const scriptName = name.slice(0, -3) ;[...wildScriptUsers_, ...scriptNamesToUsers.get(scriptName)].includes(user) && usersToScriptsToPush.get(user).set(scriptName, path) } }) ) for (const [scriptName, users] of scriptNamesToUsers) for (const user of users) if (!usersToScriptsToPush.get(user).has(scriptName)) return new NoScriptsError(`Could not find script ${user}.${scriptName} to push`) const pathsToUsers = new AutoMap(_path => new Set()) for (const [user, scriptsToPush] of usersToScriptsToPush) for (const path of scriptsToPush.values()) pathsToUsers.get(path).add(user) const allInfo = [] await Promise.all( [...pathsToUsers].map(async ([path, [...users]]) => { const scriptName = basename(path.slice(0, -3)), uniqueId = Math.floor(Math.random() * 2 ** 52) .toString(36) .padStart(11, "0"), { script: minifiedCode, warnings } = await processScript(await readFile(path, { encoding: "utf8" }), { minify, scriptUser: !0, scriptName, uniqueId, filePath: path, mangleNames, forceQuineCheats, rootFolderPath }), info = { path, users, characterCount: countHackmudCharacters(minifiedCode), error: void 0, warnings } await Promise.all( users.map(user => writeFilePersistent( resolve(hackmudPath, user, `scripts/${scriptName}.js`), minifiedCode .replace(RegExp(`\\$${uniqueId}\\$SCRIPT_USER\\$`, "g"), user) .replace(RegExp(`\\$${uniqueId}\\$FULL_SCRIPT_NAME\\$`, "g"), `${user}.${scriptName}`) ) ) ) allInfo.push(info) onPush(info) }) ) return allInfo } export { MissingHackmudFolderError, MissingSourceFolderError, NoScriptsError, NoUsersError, push }