How to Execute JavaScript Line by Line While Preserving Scope?

II am building a custom JavaScript debugger that allows users to execute code line by line, maintain scope across lines, and implement debugger functionalities like stepping into/over code, breakpoints, etc.

I attempted to execute code line by line using eval, but I encountered scope issues where variables and functions declared on one line were not accessible on subsequent lines. Here’s a minimal reproducible example of the problem:

let codeString = `
  function test(param){
    return param;
  }
  console.log(test("param"));
`;

let codeLines = codeString.split("n"); // Split code into lines

function execute() {
  for (let line of codeLines) {
    if (line.trim()) {
      try {
        eval(line); // Execute each line independently
      } catch (error) {
        console.error("Error executing line:", line, error);
      }
    }
  }
}

execute();

When running this code:

  1. Scope Issue: The test function declared on the first line is not accessible to the console.log statement on the second line because eval executes each line in isolation.

  2. Expected Behavior: I want each line to be executed in a way that preserves the shared execution context (like in a real JavaScript runtime).

How can I execute JavaScript code line by line while maintaining the scope and context between lines?

I am specifically looking for:

  • A way to preserve variable/function declarations across lines.

  • A method to enable low-level control for implementing debugging features like stepping and breakpoints.

I’ve tried exploring alternatives like the Function constructor but ran into similar scope issues. Any suggestions or insights would be greatly appreciated!