UNPKG

@webwriter/code

Version:

Write and run code as a code cell. Supports several languages (HTML/CSS/JS, TypeScript, Python).

167 lines (151 loc) 5.16 kB
import { javascript } from '@codemirror/lang-javascript'; import Code from '../ww-code-javascript'; import workerurl from './jsWorker' // bind function to code cell // capture console calls // Redirect results to code cell output const javascriptWorker: Worker = new Worker(workerurl, {type: "classic"}) const callbacks: object = {}; javascriptWorker.onmessage = (event) => { const { id, ...data } = event.data; const onSuccess = callbacks[id]; delete callbacks[id]; onSuccess(data); }; const asyncRun = (() => { let id = 0; return (script: any, context: any) => { id = (id + 1) % Number.MAX_SAFE_INTEGER; return new Promise((onSuccess) => { callbacks[id] = onSuccess; javascriptWorker.postMessage({ ...context, code: script, id, }); }); }; })(); const executeJavascript = async (code: string, context: Code) => { const oldConsole = window.console; window.console = customConsole(context); try { const result = context.globalExecution ? unScopeEval(code, context) : await asyncRun(code, "undefined"); context.globalExecution ? {} : JSON.parse(result["results"]).forEach(entry => {if(!context.results.includes(entry)){context.results.push(entry)}}); return result; } catch (e) { context.results.push({ color: 'red', text: e.message }); return e; } finally { window.console = oldConsole; } context.results = [...context.results]; }; function scopedEval(code: string, context: Code) { return Function(` const codeToExecute = [${code .split('\n') .map((line) => `'${line.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`) .join(',')}]; return eval(codeToExecute.join("\\n")); `)(); } function unScopeEval(code: string, context: Code) { const st = document.createElement('script'); st.innerHTML = code; if (context.runAsModule) { st.type = 'module'; } document.body.appendChild(st); document.body.removeChild(st); return undefined; } export const javascriptModule = { name: 'JS', executionFunction: executeJavascript, languageExtension: javascript(), }; function customConsole(context: Code) { return { log: (...objs: any[]) => { objs.forEach((obj) => context.results.push({ color: 'inherit', text: typeof obj === 'string' ? String(obj) : JSON.stringify(obj), }) ); }, info: (...objs: any[]) => { objs.forEach((obj) => context.results.push({ color: 'inherit', text: typeof obj === 'string' ? String(obj) : JSON.stringify(obj), }) ); }, warn: (...objs: any[]) => { objs.forEach((obj) => context.results.push({ color: 'orange', text: typeof obj === 'string' ? String(obj) : JSON.stringify(obj), }) ); }, error: (...objs: any[]) => { objs.forEach((obj) => context.results.push({ color: 'red', text: typeof obj === 'string' ? String(obj) : JSON.stringify(obj), }) ); }, trace: (...objs: any[]) => { objs.forEach((obj) => context.results.push({ color: 'inherit', text: typeof obj === 'string' ? String(obj) : JSON.stringify(obj), }) ); }, table: (...objs: any[]) => { objs.forEach((obj) => context.results.push({ color: 'inherit', text: typeof obj === 'string' ? String(obj) : JSON.stringify(obj), }) ); }, assert: (assertion: boolean, ...objs: any[]) => { assertion && objs.forEach((obj) => context.results.push({ color: 'inherit', text: typeof obj === 'string' ? String(obj) : JSON.stringify(obj), }) ); }, clear: () => {}, count: () => {}, countReset: () => {}, debug: (...objs: any[]) => { objs.forEach((obj) => context.results.push({ color: 'inherit', text: typeof obj === 'string' ? String(obj) : JSON.stringify(obj), }) ); }, dir: () => {}, dirxml: () => {}, group: () => {}, groupCollapsed: () => {}, groupEnd: () => {}, profile: () => {}, profileEnd: () => {}, time: () => {}, timeEnd: () => {}, timeLog: () => {}, timeStamp: () => {}, Console: window.console.Console, }; }