All files / packages/fds-uif/generator-lwc/src/utils conditionToExpr.ts

47.05% Statements 16/34
39.28% Branches 11/28
66.66% Functions 2/3
51.61% Lines 16/31

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 89 90 91 92 93 94                      27x             27x 27x 27x       49x 27x 27x             22x     11x   11x     11x 11x 11x 11x     11x 11x                                                                                          
import ts from 'typescript';
 
/**
 * Parse a JS-shaped condition string (e.g. `"size === 'medium'"`, `"disabled"`,
 * `"a || b"`) into a TS expression node, prefixing identifier references that
 * match `propNames` with `this.`.
 *
 * The analyzer emits these conditions as plain JS expressions over the
 * component's own props, so a small recursive rewrite is enough.
 */
export function conditionToExpression(condition: string, propNames: ReadonlySet<string>): ts.Expression {
  const sourceFile = ts.createSourceFile(
    '__cond.ts',
    `(${condition});`,
    ts.ScriptTarget.ESNext,
    true,
    ts.ScriptKind.TS,
  );
  const stmt = sourceFile.statements[0] as ts.ExpressionStatement;
  const expr = (stmt.expression as ts.ParenthesizedExpression).expression;
  return rewrite(expr, propNames);
}
 
function rewrite(node: ts.Expression, propNames: ReadonlySet<string>): ts.Expression {
  if (ts.isIdentifier(node)) {
    Eif (propNames.has(node.text)) {
      return ts.factory.createPropertyAccessExpression(
        ts.factory.createThis(),
        ts.factory.createIdentifier(node.text),
      );
    }
    return ts.factory.createIdentifier(node.text);
  }
  if (ts.isStringLiteral(node)) {
    // Clone with fresh position info so the printer emits the text instead
    // of trying to look it up in the (different) original source file.
    return ts.factory.createStringLiteral(node.text);
  }
  Iif (ts.isNumericLiteral(node)) {
    return ts.factory.createNumericLiteral(node.text);
  }
  Iif (node.kind === ts.SyntaxKind.TrueKeyword) return ts.factory.createTrue();
  Iif (node.kind === ts.SyntaxKind.FalseKeyword) return ts.factory.createFalse();
  Iif (node.kind === ts.SyntaxKind.NullKeyword) return ts.factory.createNull();
  Iif (ts.isParenthesizedExpression(node)) {
    return ts.factory.createParenthesizedExpression(rewrite(node.expression, propNames));
  }
  Eif (ts.isBinaryExpression(node)) {
    return ts.factory.createBinaryExpression(
      rewrite(node.left, propNames),
      node.operatorToken,
      rewrite(node.right, propNames),
    );
  }
  if (ts.isPrefixUnaryExpression(node)) {
    return ts.factory.createPrefixUnaryExpression(
      node.operator,
      rewrite(node.operand, propNames) as ts.UnaryExpression,
    );
  }
  if (ts.isConditionalExpression(node)) {
    return ts.factory.createConditionalExpression(
      rewrite(node.condition, propNames),
      node.questionToken,
      rewrite(node.whenTrue, propNames),
      node.colonToken,
      rewrite(node.whenFalse, propNames),
    );
  }
  if (ts.isPropertyAccessExpression(node)) {
    // Only rewrite the receiver side, not the .name (which is an identifier
    // inside the property-access slot, not a free reference).
    return ts.factory.updatePropertyAccessExpression(node, rewrite(node.expression, propNames), node.name);
  }
  if (ts.isElementAccessExpression(node)) {
    return ts.factory.updateElementAccessExpression(
      node,
      rewrite(node.expression, propNames),
      rewrite(node.argumentExpression, propNames),
    );
  }
  if (ts.isCallExpression(node)) {
    return ts.factory.updateCallExpression(
      node,
      rewrite(node.expression, propNames),
      node.typeArguments,
      node.arguments.map((a) => rewrite(a as ts.Expression, propNames)),
    );
  }
  // Literals (string, numeric, boolean, null/undefined, etc.) pass through
  // untouched.
  return node;
}