8ar8az/Gendiff-Hexlet

View on GitHub
src/renderers/pretty.js

Summary

Maintainability
A
0 mins
Test Coverage
import _ from 'lodash';
import getValueType from './value-typify';

const standardIndentSize = 4;

const getIndent = depth => _.repeat(' ', depth * standardIndentSize);

const formatCharacters = {
  added: '+',
  removed: '-',
  unmodified: ' ',
};

const blockCharacters = {
  opened: '{',
  closed: '}',
};

const valueRenderMethods = {
  primitive: _.identity,
  string: _.identity,

  map: (value, depth) => {
    const indent = getIndent(depth);
    const renderKeyValuePair = key => `${indent}    ${key}: ${value[key]}`;
    const renderedKeyValuePairs = _.keys(value).map(renderKeyValuePair);
    return [blockCharacters.opened, ...renderedKeyValuePairs, `${indent}${blockCharacters.closed}`].join('\n');
  },
};

const getValueString = (name, value, depth, formatCharacter) => {
  const valueType = getValueType(value);
  const renderedValue = valueRenderMethods[valueType](value, depth + 1);
  const indent = getIndent(depth);
  return `${indent}  ${formatCharacter} ${name}: ${renderedValue}`;
};

const astNodesRenderMethods = {
  added: (
    { name, valueAfter },
    depth,
  ) => getValueString(name, valueAfter, depth, formatCharacters.added),

  removed: (
    { name, valueBefore },
    depth,
  ) => getValueString(name, valueBefore, depth, formatCharacters.removed),

  modified: ({ name, valueAfter, valueBefore }, depth) => {
    const valueBeforeString = getValueString(name, valueBefore, depth, formatCharacters.removed);
    const valueAfterString = getValueString(name, valueAfter, depth, formatCharacters.added);

    return [valueBeforeString, valueAfterString];
  },

  unmodified: (
    { name, valueBefore },
    depth,
  ) => getValueString(name, valueBefore, depth, formatCharacters.unmodified),

  composited: ({ name, children }, depth, renderAst) => {
    const indent = getIndent(depth);
    return `${indent}    ${name}: ${renderAst(children, depth + 1)}`;
  },
};

const renderAst = (ast, depth = 0) => {
  const renderAstNode = astNode => astNodesRenderMethods[astNode.type](astNode, depth, renderAst);
  const renderedAst = _.flatten(ast.map(renderAstNode));

  const indent = getIndent(depth);
  return [blockCharacters.opened, ...renderedAst, `${indent}${blockCharacters.closed}`].join('\n');
};

export default renderAst;