Source: scope.js

const visit = require("ast-types").visit;

/**
 * Adds dependencies to the given dAst object based on the function calls in the AST.
 *
 * @param {Object} dAst - The dAst object.
 * @returns {Object} - The updated dAst object with added dependencies.
 */
function dependencies(dAst) {
  const functionNames = Object.keys(require("./support-lib.js"));

  dAst.dependencies = new Set([]);
  visit(dAst.ast, {
    visitCallExpression(path) {
      const node = path.node;
      let name = node.callee.name;
      if (functionNames.includes(name)) {
        dAst.dependencies.add(name);
      }
      this.traverse(path);
    }
  });

  return dAst;
}

/**
 * Analyzes the scope of a given abstract syntax tree (AST).
 *
 * @param {Object} dAst - The abstract syntax tree (AST) to analyze.
 * @returns {Object} - The modified abstract syntax tree (AST) with scope information.
 */
const scopeAnalysis = (dAst) => {
  const Scope = require('./scope-class.js');
  let scope = new Scope(null);
  let ast = dAst.ast;

  visit(ast, {
    visitFunctionExpression(path) {
      let node = path.node;
      scope = new Scope(scope);

      let params = node.params;
      for (let param of params) {
        scope.setAsInitialized(param.name);
      }
      this.traverse(path);
      if (scope.length > 0) {
        node.body.body.unshift(scope.buildDeclaration());
      }
      node.scope = scope;
      let d = scope.notDeclaredMessage();
      if (d) console.error(d + ' used in function scope');

      scope = scope.parent;
    },
    visitAssignmentExpression(path) {
      const node = path.node;
      if (node.left.type === 'Identifier') {
        let name = node.left.name;
        if (name && name && /^[$]/.test(name) && !scope.has(name)) {
          if (!dAst.dependencies.has(name)) {
            scope.add(name);
          }
        }
      }
      this.traverse(path);
    },
    visitIdentifier(path) {
      let name = path.node.name;
      if (/^[$]/.test(name) && !dAst.dependencies.has(name)) {
        scope.setAsUsed(name);
      }
      this.traverse(path);
    }
  });

  if (scope.length > 0) {
    ast.body.unshift(scope.buildDeclaration());
  }

  ast.scope = scope;
  let d = scope.notDeclaredMessage();
  if (d) console.error(d);

  return dAst;
}

module.exports = {
  scopeAnalysis,
  dependencies,
}