karlkoder-playground/src/code_runner.js

85 lines
2.1 KiB
JavaScript

export class CodeRunner {
constructor(console, codeStopper) {
this.console = console;
this.codeStopper = codeStopper;
this.isRunning = false;
this.evalScope = {};
window.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 window
const oldWindow = {};
for (const prop in this.evalScope) {
if (!this.evalScope.hasOwnProperty(prop)) continue;
oldWindow[prop] = window[prop];
window[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 oldWindow) {
if (!oldWindow.hasOwnProperty(prop)) continue;
window[prop] = oldWindow[prop];
}
}
}