96 lines
2.5 KiB
JavaScript
96 lines
2.5 KiB
JavaScript
export class CodeRunner {
|
|
constructor(console, gamelib) {
|
|
this.console = console;
|
|
this.gamelib = gamelib;
|
|
this.isRunning = false;
|
|
this.evalScope = {};
|
|
this.abortController = new AbortController();
|
|
|
|
globalThis.playgroundConsole = this.console;
|
|
}
|
|
|
|
setCode(code) {
|
|
this.code = code;
|
|
}
|
|
|
|
async run() {
|
|
this.isRunning = true;
|
|
this.abortController = new AbortController();
|
|
|
|
this.gamelib.init();
|
|
|
|
this.console.clear();
|
|
this.console.log("Running code...");
|
|
|
|
// Use RNG to prevent caching
|
|
const encodedText = encodeURIComponent(
|
|
`let console=playgroundConsole;${this.code}/*(tph): ${Math.random()}*/`,
|
|
);
|
|
|
|
try {
|
|
const module = await import(`data:text/javascript;charset=utf-8,${encodedText}`);
|
|
this.evalScope = Object.assign(this.evalScope, module);
|
|
} catch (error) {
|
|
Error.captureStackTrace(error, this.run);
|
|
|
|
if (!error.stack || error instanceof SyntaxError) {
|
|
error.stack = `${error.fileName}:${error.lineNumber}:${error.columnNumber}`;
|
|
}
|
|
|
|
this.console.error(error);
|
|
}
|
|
}
|
|
|
|
stop() {
|
|
this.isRunning = false;
|
|
this.abortController.abort();
|
|
|
|
this.gamelib.destroy();
|
|
|
|
this.console.log("Stopping code...");
|
|
}
|
|
|
|
toggle() {
|
|
if (this.isRunning) {
|
|
this.stop();
|
|
} else {
|
|
this.run();
|
|
}
|
|
}
|
|
|
|
evaluateCode(code) {
|
|
// Move evalScope to global scope
|
|
const oldGlobalThis = {};
|
|
for (const prop in this.evalScope) {
|
|
if (!Object.hasOwn(this.evalScope, prop)) continue;
|
|
|
|
oldGlobalThis[prop] = globalThis[prop];
|
|
globalThis[prop] = this.evalScope[prop];
|
|
}
|
|
|
|
// Evaluate code
|
|
const func = new Function(`"use strict";let console=playgroundConsole;return ${code}`);
|
|
|
|
try {
|
|
const result = func();
|
|
|
|
this.console.log(result);
|
|
} catch (e) {
|
|
this.console.error(e);
|
|
|
|
if (e instanceof ReferenceError) {
|
|
this.console.info(
|
|
"Note: use `export` to allow use of variables within the console",
|
|
);
|
|
}
|
|
}
|
|
|
|
// Restore old window props
|
|
for (const prop in oldGlobalThis) {
|
|
if (!Object.hasOwn(globalThis, prop)) continue;
|
|
|
|
globalThis[prop] = oldGlobalThis[prop];
|
|
}
|
|
}
|
|
}
|