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 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | 78x 78x 36x 42x 42x 116x 44x 41x 3x 72x 25x 47x 3x 44x 43x 42x 41x 1x 40x 28x 12x 3x 9x 1x 8x 4x 4x 2x 2x 2x 1x | import {
factory,
SyntaxKind,
parseExpression,
getSynthesizedDeepClone,
isIdentifier,
isStringLiteral,
isNumericLiteral,
isParenthesizedExpression,
isBinaryExpression,
isPrefixUnaryExpression,
isConditionalExpression,
isPropertyAccessExpression,
isElementAccessExpression,
isCallExpression,
type Expression,
} from './ts7.js';
/**
* 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.
*
* Under the TS 7 native API the parser lives out-of-process, so `parseExpression`
* returns "remote" nodes that cannot be spliced into a synthetic tree directly
* (the printer panics). The rewrite below therefore rebuilds every node it
* touches with the local `factory`, and clones any pass-through subtree with
* `getSynthesizedDeepClone`.
*/
export function conditionToExpression(condition: string, propNames: ReadonlySet<string>): Expression {
// Fast path for a bare prop reference (the common shape, e.g. `"disabled"`).
// This also correctly handles prop names that are JS reserved words (e.g. a
// state named `new`): the parser tokenizes a bare `new` as an incomplete
// NewExpression rather than an identifier, so `rewrite` below would miss it
// and the printer would emit a bare, invalid `new`. Emitting `this.<name>`
// directly sidesteps that. Byte-identical to the parse path for ordinary
// identifiers.
const trimmed = condition.trim();
if (propNames.has(trimmed) && /^[A-Za-z_$][\w$]*$/.test(trimmed)) {
return factory.createPropertyAccessExpression(factory.createThis(), factory.createIdentifier(trimmed));
}
const expr = parseExpression(condition);
return rewrite(expr, propNames);
}
function rewrite(node: Expression, propNames: ReadonlySet<string>): Expression {
if (isIdentifier(node)) {
if (propNames.has(node.text)) {
return factory.createPropertyAccessExpression(
factory.createThis(),
factory.createIdentifier(node.text),
);
}
return factory.createIdentifier(node.text);
}
if (isStringLiteral(node)) {
// Rebuild with the local factory so the printer emits the text instead
// of trying to look it up in the (out-of-process) original source file.
return factory.createStringLiteral(node.text);
}
if (isNumericLiteral(node)) {
return factory.createNumericLiteral(node.text);
}
if (node.kind === SyntaxKind.TrueKeyword) return factory.createTrue();
if (node.kind === SyntaxKind.FalseKeyword) return factory.createFalse();
if (node.kind === SyntaxKind.NullKeyword) return factory.createNull();
if (isParenthesizedExpression(node)) {
return factory.createParenthesizedExpression(rewrite(node.expression, propNames));
}
if (isBinaryExpression(node)) {
return factory.createBinaryExpression(
rewrite(node.left, propNames),
node.operatorToken.kind,
rewrite(node.right, propNames),
);
}
if (isPrefixUnaryExpression(node)) {
return factory.createPrefixUnaryExpression(node.operator, rewrite(node.operand, propNames));
}
if (isConditionalExpression(node)) {
return factory.createConditionalExpression(
rewrite(node.condition, propNames),
undefined,
rewrite(node.whenTrue, propNames),
undefined,
rewrite(node.whenFalse, propNames),
);
}
if (isPropertyAccessExpression(node)) {
// Only rewrite the receiver side, not the .name (which is an identifier
// inside the property-access slot, not a free reference).
return factory.createPropertyAccessExpression(
rewrite(node.expression, propNames),
factory.createIdentifier(node.name.text),
);
}
if (isElementAccessExpression(node)) {
return factory.createElementAccessExpression(
rewrite(node.expression, propNames),
rewrite(node.argumentExpression, propNames),
);
}
Eif (isCallExpression(node)) {
return factory.createCallExpression(rewrite(node.expression, propNames), undefined, [
...node.arguments.map((a) => rewrite(a as Expression, propNames)),
]);
}
// Any other node passes through untouched, but must be deep-cloned into a
// synthetic node so the printer can emit it.
return getSynthesizedDeepClone(node);
}
|