85 lines
2.1 KiB
JavaScript
85 lines
2.1 KiB
JavaScript
export class CodeRunner {
|
|
constructor(console, codeStopper) {
|
|
this.console = console;
|
|
this.codeStopper = codeStopper;
|
|
this.isRunning = false;
|
|
this.evalScope = {};
|
|
|
|
globalThis.playgroundConsole = this.console;
|
|
}
|
|
|
|
setCode(code) {
|
|
this.code = code;
|
|
}
|
|
|
|
async run() {
|
|
this.isRunning = true;
|
|
|
|
this.console.clear();
|
|
this.console.log("Running code...");
|
|
|
|
this.codeStopper.start();
|
|
|
|
// 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) {
|
|
this.console.error(error);
|
|
}
|
|
}
|
|
|
|
stop() {
|
|
this.isRunning = false;
|
|
this.codeStopper.stop();
|
|
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];
|
|
}
|
|
}
|
|
}
|