All files / style9/src generate-expression.js

100% Statements 39/39
100% Branches 12/12
100% Functions 12/12
100% Lines 36/36

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 77 78 798x 8x 8x     51x         51x 59x 59x 59x   59x 58x   54x 38x     1x 1x   37x     38x     16x       16x     12x 11x     12x       35x 44x         64x       35x 51x 51x 50x   49x 15x       34x 1x     33x 16x     33x     8x  
const { mapObject, removeDuplicates } = require('./utils/helpers');
const t = require('@babel/types');
const assert = require('assert');
 
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];
 
    assert(name in classes, `Property ${name} does not exist in style object`);
    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;