UNPKG

hackmud-script-manager

Version:

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

108 lines (107 loc) 3.8 kB
import babelGenerator from "@babel/generator" import { parse } from "@babel/parser" import babelTraverse from "@babel/traverse" import t from "@babel/types" import { assert } from "@samual/lib/assert" import { spliceString } from "@samual/lib/spliceString" import { resolve } from "import-meta-resolve" const { default: traverse } = babelTraverse, { default: generate } = babelGenerator async function preprocess(code, { uniqueId = "00000000000" } = {}) { assert(/^\w{11}$/.test(uniqueId), "src/processScript/preprocess.ts:23:36") const sourceCode = code let lengthBefore, file, program do { lengthBefore = code.length code = code .replace(/^\s+/, "") .replace(/^\/\/.*/, "") .replace(/^\/\*[\s\S]*?\*\//, "") } while (code.length != lengthBefore) code = code.replace(/^function\s*\(/, "export default function (") for (;;) { let error try { file = parse(code, { plugins: [ "typescript", ["decorators", { decoratorsBeforeExport: !0 }], "doExpressions", "functionBind", "functionSent", "partialApplication", ["pipelineOperator", { proposal: "hack", topicToken: "%" }], "throwExpressions", ["recordAndTuple", { syntaxType: "hash" }], "classProperties", "classPrivateProperties", "classPrivateMethods", "logicalAssignment", "numericSeparator", "nullishCoalescingOperator", "optionalChaining", "optionalCatchBinding", "objectRestSpread" ], sourceType: "module" }) break } catch (error_) { assert(error_ instanceof SyntaxError, "src/processScript/preprocess.ts:67:42") error = error_ } if ("BABEL_PARSER_SYNTAX_ERROR" != error.code || "PrivateInExpectedIn" != error.reasonCode) { console.log(/.+/.exec(code.slice(error.pos))?.[0]) throw error } const codeSlice = code.slice(error.pos) let match if ((match = /^#[0-4fhmln]s\.scripts\.quine\(\)/.exec(codeSlice))) code = spliceString(code, JSON.stringify(sourceCode), error.pos, match[0].length) else if ((match = /^#[0-4fhmln]?s\./.exec(codeSlice))) code = spliceString(code, "$", error.pos, 1) else if ((match = /^#D[^\w$]/.exec(codeSlice))) code = spliceString(code, "$", error.pos, 1) else if ((match = /^#FMCL/.exec(codeSlice))) code = spliceString(code, `$${uniqueId}$FMCL$`, error.pos, match[0].length) else if ((match = /^#G/.exec(codeSlice))) code = spliceString(code, `$${uniqueId}$GLOBAL$`, error.pos, match[0].length) else { if (!(match = /^#db\./.exec(codeSlice))) throw error code = spliceString(code, "$", error.pos, 1) } } traverse(file, { Program(path) { program = path path.skip() } }) const needRecord = program.scope.hasGlobal("Record"), needTuple = program.scope.hasGlobal("Tuple") ;(needRecord || needTuple) && file.program.body.unshift( t.importDeclaration( needRecord ? needTuple ? [ t.importSpecifier(t.identifier("Record"), t.identifier("Record")), t.importSpecifier(t.identifier("Tuple"), t.identifier("Tuple")) ] : [t.importSpecifier(t.identifier("Record"), t.identifier("Record"))] : [t.importSpecifier(t.identifier("Tuple"), t.identifier("Tuple"))], t.stringLiteral("@bloomberg/record-tuple-polyfill") ) ) program.scope.hasGlobal("Proxy") && file.program.body.unshift( t.importDeclaration( [t.importDefaultSpecifier(t.identifier("Proxy"))], t.stringLiteral(resolve("proxy-polyfill/src/proxy.js", import.meta.url).slice(7)) ) ) if (1 == program.node.body.length && "FunctionDeclaration" == program.node.body[0].type) throw Error( "Scripts that only contain a single function declaration are no longer supported.\nPrefix the function declaration with `export default`." ) return { code: generate(file).code } } export { preprocess }