@@ -22,6 +22,23 @@ class JavascriptExecutor implements ReadableStreamDefaultReader<string> {
2222 } )
2323 }
2424
25+ private getIframeWindow ( ) {
26+ const id = 'code-runner-javascript-vm'
27+
28+ // clean up
29+ document . getElementById ( id ) ?. remove ( )
30+
31+ const iframe = document . createElement ( 'iframe' )
32+ iframe . id = id
33+ iframe . style . display = 'none'
34+ document . body . appendChild ( iframe )
35+ const iframeWindow = iframe . contentWindow ! as Window & typeof globalThis
36+
37+ iframeWindow . ctx = window . ctx
38+
39+ return iframeWindow
40+ }
41+
2542 private async runCode ( ) : Promise < undefined > {
2643 const stringify = ( args : any [ ] ) => args . map ( ( arg ) => {
2744 if ( [ 'boolean' , 'number' , 'bigint' , 'string' , 'symbol' , 'function' ] . includes ( typeof arg ) ) {
@@ -37,18 +54,23 @@ class JavascriptExecutor implements ReadableStreamDefaultReader<string> {
3754 this . _readResolve ( str )
3855 }
3956
40- // eslint-disable-next-line no-eval
41- await eval ( `(async () => {
42- const console = new Proxy(window.console, {
43- get: (obj, prop) => ['error', 'warn', 'info', 'log', 'debug'].includes(prop)
44- ? (...args) => {
45- obj[prop](...args);
46- ${ tick . name } (args);
57+ const iframeWindow = this . getIframeWindow ( )
58+ const xConsole = new Proxy ( iframeWindow . console , {
59+ get : ( obj : any , prop : any ) => [ 'error' , 'warn' , 'info' , 'log' , 'debug' ] . includes ( prop )
60+ ? ( ...args : any [ ] ) => {
61+ obj [ prop ] ( ...args )
62+ if ( prop === 'log' ) {
63+ tick ( args )
64+ } else {
65+ tick ( [ `${ prop [ 0 ] } :` , ...args ] )
66+ }
4767 }
48- : obj[prop]
49- });
50- ${ this . code }
51- })()` )
68+ : obj [ prop ]
69+ } )
70+
71+ const AsyncFunction = iframeWindow . eval ( '(async function(){}).constructor' )
72+ const fn = new AsyncFunction ( 'console' , 'ctx' , this . code )
73+ await fn . call ( iframeWindow , xConsole , window . ctx )
5274
5375 await sleep ( 0 )
5476 this . _readResolve ( '' )
@@ -87,16 +109,18 @@ export default {
87109 register : ( ctx ) => {
88110 ctx . runner . registerRunner ( {
89111 name : 'javascript' ,
90- order : 255 ,
112+ order : 100 ,
91113 match ( language ) {
92114 return [ 'js' , 'javascript' ] . includes ( language . toLowerCase ( ) )
93115 } ,
94116 getTerminalCmd ( ) {
95117 return null
96118 } ,
97119 async run ( _ , code ) {
120+ const firstLine = code . split ( '\n' ) [ 0 ] . trim ( )
121+ const outputHtml = firstLine . includes ( '--output-html--' )
98122 return {
99- type : 'html' ,
123+ type : outputHtml ? 'html' : 'plain ',
100124 value : new JavascriptExecutor ( code )
101125 }
102126 } ,
0 commit comments