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.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";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]; } } }