如何从 `script.runInContext` 的结果中获取 AST 表示(无损)?

问题描述 投票:0回答:2

我正在做类似的事情

let p = `(function (X) {
  var q = 0;
  for (; q < 5; ) {
    // do some stuff
    anotherfunc = function (a) {
      while (X === "somestring") {
        X += "_";
      }
    };
    q++;
  }
  return { anotherfunc: anotherfunc };
})("some arg");
`;

const vm = require("vm");
const script = new vm.Script(p);
const context = vm.createContext({});
const result = script.runInContext(context);

console.log(result);

我得到

{ anotherfunc: ƒ anotherfunc() }
打印,但如果我使用
JSON.stringify(result)
我得到
"{}"

我想要得到类似的东西

{ 
    anotherfunc: function (a) {
      while (X === "somestring") {
        X += "_";
      }
    };
}

为了将其传递给

acorn.parse
并获取 AST 表示。

有没有办法让

script.runInContext
将结果保存到文件中,或者让
acorn
解析不是字符串的
result

javascript node.js abstract-syntax-tree acorn
2个回答
0
投票

尝试一下。

const vm = require("vm");
const acorn = require("acorn");

let p = `(function (X) {
  var q = 0;
  for (; q < 5; ) {
    // do some stuff
    anotherfunc = function (a) {
      while (X === "somestring") {
        X += "_";
      }
    };
    q++;
  }
  return { anotherfunc: anotherfunc };
})("some arg");
`;

const script = new vm.Script(p);
const context = vm.createContext({});
const result = script.runInContext(context);

// Extract the function source code
const functionSourceCode = result.anotherfunc.toString();

// Parse the function source code using acorn
const ast = acorn.parse(functionSourceCode, { ecmaVersion: 2020 });

console.log(JSON.stringify(ast, null, 2));

-1
投票

我最终得到的当前解决方案如下(在 ChatGPT 的帮助下)。请随意提出其他更强大的解决方案......或改进这个:

const vm = require("vm");

// Example code to run
let p = `(function (X) {
  var q = 0;
  for (; q < 5; ) {
    anotherfunc = function (a) {
      while (X === "somestring") {
        X += "_";
      }
    };
    q++;
  }
  return { anotherfunc: anotherfunc, nested: { funcInside: function() { return "inner"; }, num: 42 }, str: "hello" };
})("some arg");
`;

const script = new vm.Script(p);
const context = vm.createContext({});
const result = script.runInContext(context);

// Recursive function to handle all types
function stringifyResult(value) {
  // 1. Handle functions first (convert function to string)
  if (typeof value === 'function') {
    return value.toString();
  }

  // 2. Base case for primitives (string, number, etc.)
  if (value === null || value === undefined || typeof value !== 'object') {
    return JSON.stringify(value);
  }

  // 3. Handle arrays
  if (Array.isArray(value)) {
    return `[${value.map(item => stringifyResult(item)).join(', ')}]`;
  }

  // 4. Handle objects (including nested objects)
  if (typeof value === 'object') {
    let result = '{';
    for (let key in value) {
      if (value.hasOwnProperty(key)) {
        result += `${JSON.stringify(key)}: ${stringifyResult(value[key])}, `;
      }
    }
    result = result.slice(0, -2) + '}'; // Remove the last comma and space
    return result;
  }

  return '{}';
}

// Apply the function to the result
const stringifiedResult = stringifyResult(result);

// Log the result (string representation of the entire structure)
console.log(stringifiedResult);
© www.soinside.com 2019 - 2024. All rights reserved.