All files / style9/src/helpers generate-expression.js

100% Statements 37/37
100% Branches 12/12
100% Functions 12/12
100% Lines 34/34

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 778x 8x     53x         53x 61x 61x 61x   61x   57x 41x     1x 1x   40x     41x     16x       16x     12x 11x     12x       37x 49x         69x       37x 53x 53x 53x   52x 15x       37x 1x     36x 16x     36x     8x  
const t = require('@babel/types');
const { mapObject, removeDuplicates } = require('../utils/helpers');
 
function getConditionalArgs(args, classes) {
  const newArgs = [];
  let prevValue;
 
  // Iterate over args backwards. If a string literal is found, it means the
  // property is applied unconditionally, and the rest can be skipped
  for (let n = args.length - 1; n >= 0; n--) {
    const arg = args[n];
    const name = typeof arg === 'string' ? arg : arg.value;
    const cls = classes[name];
 
    if (cls === undefined) continue;
 
    if (typeof arg === 'string') {
      if (prevValue === cls) {
        // If the last last value is the same as the static value, the last
        // conditional can be skipped since both sides would be the same
        const last = newArgs.pop();
        newArgs.push(last.value);
      } else {
        newArgs.push(t.stringLiteral(cls + ' '));
      }
 
      return newArgs;
    }
 
    newArgs.push({
      test: arg.test,
      value: t.stringLiteral(cls + ' ')
    });
    prevValue = cls;
  }
 
  if (newArgs.length) {
    newArgs.push(t.stringLiteral(''));
  }
 
  return newArgs;
}
 
function listObjectsProperties(classObj) {
  return removeDuplicates(
    Object.values(classObj).flatMap(obj => Object.keys(obj))
  );
}
 
function getObjectsProp(object, prop) {
  return mapObject(object, ([key, val]) => [key, val[prop]]);
}
 
function generateExpression(args, classObject) {
  const conditionals = listObjectsProperties(classObject)
    .map(prop => getObjectsProp(classObject, prop))
    .map(classes => getConditionalArgs(args, classes))
    .filter(conditionalArgs => conditionalArgs.length)
    .map(conditionalArgs =>
      conditionalArgs.reduceRight((acc, prop) =>
        t.conditionalExpression(prop.test, prop.value, acc)
      )
    );
 
  if (!conditionals.length) {
    return t.stringLiteral('');
  }
 
  const binaryExpression = conditionals.reduceRight((acc, expr) =>
    t.binaryExpression('+', expr, acc)
  );
 
  return t.expressionStatement(binaryExpression);
}
 
module.exports = generateExpression;