|
1 | | -// Use indirect eval so that var declarations go to the global scope, |
2 | | -// matching the behaviour of a REPL where variables persist across calls. |
3 | | -// Security note: eval is intentionally used here to implement a JavaScript |
4 | | -// REPL. This package must only be loaded in an isolated context (e.g. a Web |
5 | | -// Worker or a sandboxed Node.js process) where arbitrary code execution is |
6 | | -// the expected behaviour. |
7 | | -// eslint-disable-next-line no-eval |
8 | | -const indirectEval: (code: string) => unknown = (0, eval); |
9 | | - |
10 | | -export async function replLikeEval(code: string): Promise<unknown> { |
11 | | - // eval()の中でconst,letを使って変数を作成した場合、 |
12 | | - // 次に実行するコマンドはスコープ外扱いでありアクセスできなくなってしまうので、 |
13 | | - // varに置き換えている |
14 | | - if (code.trim().startsWith("const ")) { |
15 | | - code = "var " + code.trim().slice(6); |
16 | | - } else if (code.trim().startsWith("let ")) { |
17 | | - code = "var " + code.trim().slice(4); |
18 | | - } |
19 | | - // eval()の中でclassを作成した場合も同様 |
20 | | - const classRegExp = /^\s*class\s+(\w+)/; |
21 | | - if (classRegExp.test(code)) { |
22 | | - code = code.replace(classRegExp, "var $1 = class $1"); |
23 | | - } |
24 | | - |
25 | | - if (code.trim().startsWith("{") && code.trim().endsWith("}")) { |
26 | | - // オブジェクトは ( ) で囲わなければならない |
27 | | - try { |
28 | | - return indirectEval(`(${code})`); |
29 | | - } catch (e) { |
30 | | - if (e instanceof SyntaxError) { |
31 | | - // オブジェクトではなくブロックだった場合、再度普通に実行 |
32 | | - return indirectEval(code); |
33 | | - } else { |
34 | | - throw e; |
35 | | - } |
36 | | - } |
37 | | - } else if (/^\s*await\W/.test(code)) { |
38 | | - // promiseをawaitする場合は、promiseの部分だけをevalし、それを外からawaitする |
39 | | - return await (indirectEval(code.trim().slice(5)) as Promise<unknown>); |
40 | | - } else { |
41 | | - return indirectEval(code); |
42 | | - } |
43 | | -} |
44 | | - |
45 | | -export async function checkSyntax( |
46 | | - code: string |
47 | | -): Promise<{ status: "complete" | "incomplete" | "invalid" }> { |
48 | | - try { |
49 | | - indirectEval(`() => {${code}}`); |
50 | | - return { status: "complete" }; |
51 | | - } catch (e) { |
52 | | - if (e instanceof SyntaxError) { |
53 | | - if ( |
54 | | - e.message.includes("Unexpected token '}'") || |
55 | | - e.message.includes("Unexpected end of input") |
56 | | - ) { |
57 | | - return { status: "incomplete" }; |
58 | | - } else { |
59 | | - return { status: "invalid" }; |
60 | | - } |
61 | | - } else { |
62 | | - return { status: "invalid" }; |
63 | | - } |
64 | | - } |
65 | | -} |
| 1 | +export { replLikeEval } from "./eval.js"; |
| 2 | +export { checkSyntax } from "./syntax.js"; |
0 commit comments