All files / style9/src/helpers validate.js

100% Statements 35/35
100% Branches 24/24
100% Functions 8/8
100% Lines 29/29

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 79 80 81 82 83 84 85 86 87 88 898x 8x         8x     1x                             70x 62x 62x 17x 15x           1x                   132x 132x 2x     130x 30x     100x       94x   132x 132x   131x   27x   2x 2x 25x 1x                       76x 95x 94x       8x  
const assert = require('assert');
const testASTShape = require('../utils/test-ast-shape');
const {
  isAtRuleObject,
  isAtRule,
  isPseudoSelector
} = require('../utils/styles');
 
function isHMR(identifier) {
  return testASTShape(identifier.parentPath, {
    type: 'CallExpression',
    callee: {
      type: 'MemberExpression',
      object: {
        name: 'reactHotLoader'
      },
      property: {
        name: 'register'
      }
    }
  });
}
 
function validateReferences(references) {
  references.forEach(ref => {
    const { parentPath, parent, node } = ref;
    if (parentPath.isCallExpression() && parent.callee === node) return;
    if (parentPath.isSpreadElement()) return;
    if (parentPath.isMemberExpression()) return;
 
    // The return value from `style9.create` should be a function, but the
    // compiler turns it into an object. Therefore only access to properties
    // is allowed. React Hot Loader accesses all bindings, so a temporary
    // workaround is required. React Fast Refresh does not have this problem.
    assert(
      isHMR(ref),
      ref.buildCodeFrameError(
        'Return value from style9.create has to be called as a function or accessed as an object'
      )
    );
  });
}
 
function evalKey(objProp) {
  const keyPath = objProp.get('key');
  if (objProp.node.computed) {
    return keyPath.evaluate();
  }
 
  if (keyPath.isStringLiteral()) {
    return { value: keyPath.node.value, confident: true };
  }
 
  return { value: keyPath.node.name, confident: true };
}
 
function validateStyleObjectInner(objExpr) {
  objExpr.traverse({
    ObjectProperty(path) {
      const { value, confident } = evalKey(path);
      if (!confident) return;
 
      if (!path.get('value').isObjectExpression()) return;
 
      if (isAtRuleObject(value)) {
        // Skip direct props
        validateStyleObject(path.get('value'));
        path.skip();
      } else if (!isAtRule(value) && !isPseudoSelector(value)) {
        throw path
          .get('key')
          .buildCodeFrameError(
            `Invalid key ${value}. Object keys must be at-rules or pseudo selectors`
          );
      }
    }
  });
}
 
// Does not validate spread elements
function validateStyleObject(objExpr) {
  objExpr.get('properties').forEach(prop => {
    if (!prop.isObjectProperty()) return;
    validateStyleObjectInner(prop.get('value'));
  });
}
 
module.exports = { validateReferences, validateStyleObject };