/* eslint-disable sort-keys */
// since our usage is fairly narrow, we don't really need to install extra deps such ast-types or @babel/types.
// the set of builders I've prepared here should be sufficient for our needs
function program(body) {
  return {
    type: 'Program',
    body
  };
}
function blockStatement(body, directives) {
  return {
    type: 'BlockStatement',
    body,
    directives
  };
}
function expressionStatement(expression) {
  return {
    type: 'ExpressionStatement',
    expression
  };
}
function literal(value) {
  switch (typeof value) {
    case 'number':
      return numericLiteral(value);

    case 'string':
      return stringLiteral(value);

    case 'boolean':
      return booleanLiteral(value);
  }
}
function stringLiteral(value) {
  return {
    type: 'StringLiteral',
    value
  };
}
function booleanLiteral(value) {
  return {
    type: 'BooleanLiteral',
    value
  };
}
function numericLiteral(value) {
  return {
    type: 'NumericLiteral',
    value
  };
}
function nullLiteral() {
  return {
    type: 'NullLiteral',
    value: null
  };
}
function regExpLiteral(pattern, flags = '') {
  return {
    type: 'RegExpLiteral',
    pattern,
    flags
  };
}
function identifier(name) {
  return {
    type: 'Identifier',
    name
  };
}
function logicalExpression(operator, left, right) {
  return {
    type: 'LogicalExpression',
    operator,
    left,
    right
  };
}
function conditionalExpression(test, consequent, alternate) {
  return {
    type: 'ConditionalExpression',
    test,
    consequent,
    alternate
  };
}
function ifStatement(test, consequent, alternate) {
  return {
    type: 'IfStatement',
    test,
    consequent,
    alternate
  };
}
function binaryExpression(operator, left, right) {
  return {
    type: 'BinaryExpression',
    operator,
    left,
    right
  };
}
function safeBinaryExpression(operator, left, right) {
  let actualRight = right;

  if (right.type === 'NumericLiteral') {
    actualRight = stringLiteral(String(right.value));
  } else if (right.type === 'StringLiteral' && Number.isSafeInteger(Number(right.value))) {
    actualRight = stringLiteral(String(right.value));
  }

  return {
    type: 'BinaryExpression',
    operator,
    left: actualRight === right ? left : callExpression(identifier('String'), [left]),
    right: actualRight
  };
}
function unaryExpression(operator, argument, prefix = true) {
  return {
    type: 'UnaryExpression',
    operator,
    argument,
    prefix
  };
}
function memberExpression(object, property, computed = false, optional = null) {
  return {
    type: 'MemberExpression',
    object,
    property,
    computed,
    optional
  };
}
function assignmentExpression(operator, left, right) {
  return {
    type: 'AssignmentExpression',
    operator,
    left,
    right
  };
}
function callExpression(callee, _arguments) {
  return {
    type: 'CallExpression',
    callee,
    arguments: _arguments
  };
}
function functionDeclaration(id, params, body) {
  return {
    type: 'FunctionDeclaration',
    id,
    params,
    body
  };
}
function returnStatement(argument) {
  return {
    type: 'ReturnStatement',
    argument
  };
}
function sequenceExpression(expressions) {
  return {
    type: 'SequenceExpression',
    expressions
  };
}
function forOfStatement(left, right, body, _await) {
  return {
    type: 'ForOfStatement',
    left,
    right,
    body,
    await: _await
  };
}
function arrayExpression(elements) {
  return {
    type: 'ArrayExpression',
    elements
  };
}
function objectExpression(properties) {
  return {
    type: 'ObjectExpression',
    properties
  };
}
function objectMethod(kind, key, params, body, computed = false, generator = false, _async = false) {
  return {
    type: 'ObjectMethod',
    kind,
    key,
    params,
    body,
    computed,
    generator,
    async: _async
  };
}
function objectProperty(key, value, computed = false, shorthand = false, decorators = null) {
  return {
    type: 'ObjectProperty',
    key,
    value,
    computed,
    shorthand,
    decorators
  };
}
function variableDeclaration(kind, declarations) {
  return {
    type: 'VariableDeclaration',
    kind,
    declarations
  };
}
function variableDeclarator(id, init) {
  return {
    type: 'VariableDeclarator',
    id,
    init
  };
}
function newExpression(callee, _arguments) {
  return {
    type: 'NewExpression',
    callee,
    arguments: _arguments
  };
}
function importDeclaration(specifiers, source) {
  return {
    type: 'ImportDeclaration',
    specifiers,
    source
  };
}
function importSpecifier(local, imported) {
  return {
    type: 'ImportSpecifier',
    local,
    imported
  };
}
function exportDefaultDeclaration(declaration) {
  return {
    type: 'ExportDefaultDeclaration',
    declaration
  };
}
function arrowFunctionExpression(params, body, _async = false) {
  return {
    type: 'ArrowFunctionExpression',
    params,
    body,
    async: _async
  };
}
function tryStatement(block, handler = null, finalizer = null) {
  return {
    type: 'TryStatement',
    block,
    handler,
    finalizer
  };
}
function templateElement(value, tail = false) {
  return {
    type: 'TemplateElement',
    value,
    tail
  };
}
function templateLiteral(quasis, expressions) {
  return {
    type: 'TemplateLiteral',
    quasis,
    expressions
  };
}

export { arrayExpression, arrowFunctionExpression, assignmentExpression, binaryExpression, blockStatement, booleanLiteral, callExpression, conditionalExpression, exportDefaultDeclaration, expressionStatement, forOfStatement, functionDeclaration, identifier, ifStatement, importDeclaration, importSpecifier, literal, logicalExpression, memberExpression, newExpression, nullLiteral, numericLiteral, objectExpression, objectMethod, objectProperty, program, regExpLiteral, returnStatement, safeBinaryExpression, sequenceExpression, stringLiteral, templateElement, templateLiteral, tryStatement, unaryExpression, variableDeclaration, variableDeclarator };
