Home Reference Source

lib/ast/sagas/index.js

import debugModule from "debug";
const debug = debugModule("debugger:ast:sagas");

import { all, call, race, fork, join, take, takeEvery, put, select } from "redux-saga/effects";

import { prefixName } from "lib/helpers";

import * as data from "lib/data/sagas";

import * as actions from "../actions";

import ast from "../selectors";


function *walk(sourceId, node, pointer = "", parentId = null) {
  debug("walking %o %o", pointer, node);

  yield *handleEnter(sourceId, node, pointer, parentId);

  if (node instanceof Array) {
    for (let [i, child] of node.entries()) {
      yield call(walk, sourceId, child, `${pointer}/${i}`, parentId);
    }
  } else if (node instanceof Object) {
    for (let [key, child] of Object.entries(node)) {
      yield call(walk, sourceId, child, `${pointer}/${key}`, node.id);
    }
  }

  yield *handleExit(sourceId, node, pointer);
}

function *handleEnter(sourceId, node, pointer, parentId) {
  if (!(node instanceof Object)) {
    return;
  }

  debug("entering %s", pointer);

  if (node.id !== undefined) {
    debug("%s recording scope %s", pointer, node.id);
    yield *data.scope(node.id, pointer, parentId, sourceId);
  }

  switch (node.nodeType) {
    case "VariableDeclaration":
      debug("%s recording variable %o", pointer, node);
      yield *data.declare(node);
      break;
  }
}

function *handleExit(sourceId, node, pointer) {
  debug("exiting %s", pointer);

  // no-op right now
}

function *walkSaga({sourceId, ast}) {
  yield walk(sourceId, ast);
}

export function *visitAll(idx) {
  let sources = yield select(ast.views.sources);

  let tasks = yield all(
    Object.entries(sources)
      .filter( ([id, {ast}]) => !!ast )
      .map( ([id, {ast}]) => fork( () => put(actions.visit(id, ast))) )
  )

  if (tasks.length > 0) {
    yield join(...tasks);
  }

  yield put(actions.doneVisiting());
}

export function* saga() {
  yield race({
    visitor: takeEvery(actions.VISIT, walkSaga),
    done: take(actions.DONE_VISITING)
  });
}

export default prefixName("ast", saga);