syntax | Cell 18 | Cell 20 | Search

The Node.js module exports two functions, selectAst and makeExpr, which are used for parsing and manipulating JavaScript code, and require dependencies such as esprima and escodegen. The functions work together to convert code to HTML, generate string representations of ASTs, and parse code to extract its structure and content.

Run example

npm run import -- "select ast"

select ast

var esprima = require('esprima')
var escodegen = require('escodegen')
var importer = require('../Core')
var {
    selectDom,
    evaluateDom,
    treeToHtml,
    htmlToTree
} = importer.import("select tree",
"tree to html",
"html to tree")

var FUNCTION_BODY = `//FunctionDeclaration/BlockStatement`

function toString(ctx, subctx) {
    var ast = (subctx || ctx)
    if(typeof ast.ownerDocument !== 'undefined') {
        ast = htmlToTree(ast)
    }
    //escodegen.attachComments(ast, ast.comments, ast.tokens, ast.whitespace)
    return escodegen.generate(ast, {
        comment: true,
        tokens: true,
        whitespace: true,
        format: {indent: {style: '    '}, quotes: 'single'}
    })
}

function selectAst(descriptor, code) {
    if(typeof code === 'function') {
        code = code.toString()
    }
    if(typeof code === 'string') {
        try {
            if(code.match(/^import\s|\simport\s|^export\s|\sexport\s/gi)) {
                code = esprima.parseModule(code, {range: true, tokens: true,
                    comment: true, whitespace: true, strict: false})
            } else {
                code = esprima.parse(code, {range: true, tokens: true,
                    comment: true, whitespace: true, strict: false})
            }
        } catch(e) {
            console.log(e)
            throw new Error('Could not parse \n' + code)
        }
    }
    // TODO: move this conversion to selectDom for all objects?
    //   No more selectTree because jsel and friends are inconsistent.
    if(typeof code.evaluate === 'undefined'
       && typeof code.ownerDocument === 'undefined') {
        code = selectDom('//Program', treeToHtml(code))
        code.ownerDocument.toString = toString.bind(code, code)
    }
    return selectDom(descriptor, code)
}

function makeExpr(code) {
    if(typeof code === 'function') {
        return selectAst([FUNCTION_BODY], code.toString())[0]
    }
    return selectAst([`//ExpressionStatement/*`], `(${code})`)[0]
}

module.exports = {
    selectAst,
    makeExpr
}

What the code could have been:

const { parse } = require('esprima');
const { generate } = require('escodegen');
const {
  selectDom,
  evaluateDom,
  treeToHtml,
  htmlToTree
} = require('../Core').import(['select tree', 'tree to html', 'html to tree']);

const FUNCTION_BODY = '//FunctionDeclaration/BlockStatement';
const QUOTES ='single';
const INDENT_STYLE ='   ';

function toString(ast, subctx) {
  if (ast.ownerDocument) {
    return generate(ast, {
      comment: true,
      tokens: true,
      whitespace: true,
      format: {
        indent: { style: INDENT_STYLE },
        quotes: QUOTES
      }
    });
  }
  return generate(ast, {
    comment: true,
    tokens: true,
    whitespace: true,
    format: {
      indent: { style: INDENT_STYLE },
      quotes: QUOTES
    }
  });
}

function selectAst(descriptor, code) {
  if (typeof code === 'function') {
    code = code.toString();
  }

  try {
    if (code.includes('import') || code.includes('export')) {
      code = parseModule(code, { range: true, tokens: true, comment: true, whitespace: true, strict: false });
    } else {
      code = parse(code, { range: true, tokens: true, comment: true, whitespace: true, strict: false });
    }
  } catch (error) {
    console.error(error);
    throw new Error(`Could not parse: \n${code}`);
  }

  if (!code.evaluate &&!code.ownerDocument) {
    code = selectDom('//Program', treeToHtml(code));
    Object.defineProperty(code, 'toString', {
      value: toString.bind(code, code),
      enumerable: false,
      writable: false
    });
  }
  return selectDom(descriptor, code);
}

function makeExpr(code) {
  if (typeof code === 'function') {
    return selectAst([FUNCTION_BODY], code.toString())[0];
  }
  return selectAst([`//ExpressionStatement/*`], `(${code})`)[0];
}

module.exports = {
  selectAst,
  makeExpr
};

Code Breakdown

Overview

The provided code is a Node.js module that exports two functions: selectAst and makeExpr. These functions appear to be used for parsing and manipulating JavaScript code.

Dependencies

The code requires the following dependencies:

Functions

toString(ctx, subctx)

This function takes an AST and its optional subcontext as input. It converts the AST to HTML using htmlToTree, if necessary, and then generates a string representation of the AST using escodegen.generate.

selectAst(descriptor, code)

This function takes a descriptor and a code string (or a function that returns a code string) as input. It attempts to parse the code using esprima.parseModule or esprima.parse, depending on whether the code contains import/export statements. If parsing fails, it logs an error and throws a new error. If the code is a function, it converts it to a string. It then converts the code to an AST, if necessary, and passes it to selectDom along with the descriptor.

makeExpr(code)

This function takes a code string (or a function that returns a code string) as input. If the code is a function, it calls selectAst with the function body descriptor (FUNCTION_BODY) and the code string. Otherwise, it calls selectAst with a descriptor that matches an expression statement and wraps the code string in parentheses. The resulting AST is returned.

Exports

The module exports the selectAst and makeExpr functions.