22
33import type { ReplOutput } from "../repl" ;
44import type { MessageType , WorkerRequest , WorkerResponse } from "./runtime" ;
5+ import inspect from "object-inspect" ;
56
7+ function format ( ...args : unknown [ ] ) : string {
8+ // TODO: console.logの第1引数はフォーマット指定文字列を取ることができる
9+ // https://nodejs.org/api/util.html#utilformatformat-args
10+ return args . map ( ( a ) => ( typeof a === "string" ? a : inspect ( a ) ) ) . join ( " " ) ;
11+ }
612let jsOutput : ReplOutput [ ] = [ ] ;
713
814// Helper function to capture console output
915const originalConsole = self . console ;
1016self . console = {
1117 ...originalConsole ,
12- // eslint-disable-next-line @typescript-eslint/no-explicit-any
13- log : ( ...args : any [ ] ) => {
14- jsOutput . push ( { type : "stdout" , message : args . join ( " " ) } ) ;
18+ log : ( ...args : unknown [ ] ) => {
19+ jsOutput . push ( { type : "stdout" , message : format ( ...args ) } ) ;
1520 } ,
16- // eslint-disable-next-line @typescript-eslint/no-explicit-any
17- error : ( ...args : any [ ] ) => {
18- jsOutput . push ( { type : "stderr" , message : args . join ( " " ) } ) ;
21+ error : ( ...args : unknown [ ] ) => {
22+ jsOutput . push ( { type : "stderr" , message : format ( ...args ) } ) ;
1923 } ,
20- // eslint-disable-next-line @typescript-eslint/no-explicit-any
21- warn : ( ...args : any [ ] ) => {
22- jsOutput . push ( { type : "stderr" , message : args . join ( " " ) } ) ;
24+ warn : ( ...args : unknown [ ] ) => {
25+ jsOutput . push ( { type : "stderr" , message : format ( ...args ) } ) ;
2326 } ,
24- // eslint-disable-next-line @typescript-eslint/no-explicit-any
25- info : ( ...args : any [ ] ) => {
26- jsOutput . push ( { type : "stdout" , message : args . join ( " " ) } ) ;
27+ info : ( ...args : unknown [ ] ) => {
28+ jsOutput . push ( { type : "stdout" , message : format ( ...args ) } ) ;
2729 } ,
2830} ;
2931
@@ -36,18 +38,49 @@ async function init({ id }: WorkerRequest["init"]) {
3638}
3739
3840async function runCode ( { id, payload } : WorkerRequest [ "runCode" ] ) {
39- const { code } = payload ;
41+ let { code } = payload ;
4042 try {
41- // Execute code directly with eval in the worker global scope
42- // This will preserve variables across calls
43- const result = self . eval ( code ) ;
43+ let result : unknown ;
44+
45+ // eval()の中でconst,letを使って変数を作成した場合、
46+ // 次に実行するコマンドはスコープ外扱いでありアクセスできなくなってしまうので、
47+ // varに置き換えている
48+ if ( code . trim ( ) . startsWith ( "const " ) ) {
49+ code = "var " + code . trim ( ) . slice ( 6 ) ;
50+ } else if ( code . trim ( ) . startsWith ( "let " ) ) {
51+ code = "var " + code . trim ( ) . slice ( 4 ) ;
52+ }
53+ // eval()の中でclassを作成した場合も同様
54+ const classRegExp = / ^ \s * c l a s s \s + ( \w + ) / ;
55+ if ( classRegExp . test ( code ) ) {
56+ code = code . replace ( classRegExp , "var $1 = class $1" ) ;
57+ }
4458
45- if ( result !== undefined ) {
46- jsOutput . push ( {
47- type : "return" ,
48- message : String ( result ) ,
49- } ) ;
59+ if ( code . trim ( ) . startsWith ( "{" ) && code . trim ( ) . endsWith ( "}" ) ) {
60+ // オブジェクトは ( ) で囲わなければならない
61+ try {
62+ result = self . eval ( `(${ code } )` ) ;
63+ } catch ( e ) {
64+ if ( e instanceof SyntaxError ) {
65+ // オブジェクトではなくブロックだった場合、再度普通に実行
66+ result = self . eval ( code ) ;
67+ } else {
68+ throw e ;
69+ }
70+ }
71+ } else if ( / ^ \s * a w a i t \W / . test ( code ) ) {
72+ // promiseをawaitする場合は、promiseの部分だけをevalし、それを外からawaitする
73+ result = await self . eval ( code . trim ( ) . slice ( 5 ) ) ;
74+ } else {
75+ // Execute code directly with eval in the worker global scope
76+ // This will preserve variables across calls
77+ result = self . eval ( code ) ;
5078 }
79+
80+ jsOutput . push ( {
81+ type : "return" ,
82+ message : inspect ( result ) ,
83+ } ) ;
5184 } catch ( e ) {
5285 originalConsole . log ( e ) ;
5386 // TODO: stack trace?
@@ -110,7 +143,8 @@ async function checkSyntax({ id, payload }: WorkerRequest["checkSyntax"]) {
110143
111144 try {
112145 // Try to create a Function to check syntax
113- new Function ( code ) ;
146+ // new Function(code); // <- not working
147+ self . eval ( `() => {${ code } }` ) ;
114148 self . postMessage ( {
115149 id,
116150 payload : { status : "complete" } ,
@@ -120,8 +154,8 @@ async function checkSyntax({ id, payload }: WorkerRequest["checkSyntax"]) {
120154 if ( e instanceof SyntaxError ) {
121155 // Simple heuristic: check for "Unexpected end of input"
122156 if (
123- e . message . includes ( "Unexpected end of input " ) ||
124- e . message . includes ( "expected expression " )
157+ e . message . includes ( "Unexpected token '}' " ) ||
158+ e . message . includes ( "Unexpected end of input " )
125159 ) {
126160 self . postMessage ( {
127161 id,
0 commit comments