Skip to content

Commit

Permalink
[REFACTOR] added logic to manipulate AST to deal with negative values… (
Browse files Browse the repository at this point in the history
#1120)

* [REFACTOR] added logic to manipulate AST to deal with negative values + fixed PR comments

* [CHORE] Added changeset

* Update .changeset/lemon-ladybugs-float.md

Co-authored-by: Monica <molejniczak@atlassian.com>

* Update packages/babel-plugin/src/utils/css-builders.tsx

Co-authored-by: Monica <molejniczak@atlassian.com>

Co-authored-by: Monica <molejniczak@atlassian.com>
  • Loading branch information
csowden-atlassian and MonicaOlejniczak committed Mar 9, 2022
1 parent 356b120 commit 5e3ad5e
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/lemon-ladybugs-float.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@compiled/babel-plugin': patch
---

Fixed bug where negative and positive values were getting evaluated as the same
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions packages/babel-plugin/src/__tests__/css-builder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,25 @@ describe('css builder', () => {
"
`);
});

it('calculates a negative variable separately from a positive variable of the same value', () => {
const actual = transform(`
import { styled } from '@compiled/react';
const size = () => 8
const gridSize = size();
const LayoutRight = styled.aside\`
margin-right: -\${gridSize * 5}px;
margin-left: \${gridSize * 5}px;
\`;
<LayoutRight>Layout Right</LayoutRight>;
`);

expect(actual).toIncludeMultiple([
'margin-left:var(--_1l3fmvo)',
'margin-right:var(--_1cakqv5)',
'"--_1cakqv5": ix(-gridSize * 5, "px")',
'"--_1l3fmvo": ix(gridSize * 5, "px")',
'ax(["_2hwxsxb8 _18u01xn1", props.className]',
]);
});
});
68 changes: 67 additions & 1 deletion packages/babel-plugin/src/utils/css-builders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,64 @@ const extractLogicalExpression = (node: t.ArrowFunctionExpression, meta: Metadat
return { css: mergeSubsequentUnconditionalCssItems(css), variables };
};

const createNegativeUnaryExpression = (node: t.Identifier) => ({
type: 'UnaryExpression',
start: node.start,
end: node.end,
operator: '-',
prefix: true,
argument: {
type: 'Identifier',
start: node.start,
end: node.end,
name: node.name,
},
});

/**
* Manipulates the AST to ensure that CSS variables are generated correctly for negative values
*
* Consider we have the following:
*
* const gridSize = 8;
*
* const LayoutRight = styled.aside`
* margin-right: -${gridSize * 5}px; // A
* margin-left: ${gridSize * 5}px; // B
* top: -${gridSize * 5}px; // A
* left: -${gridSize * 8}px; // C
* right: ${gridSize * 8}px; // D
* color: red;
* `;
*
* In order to calculate the correct CSS custom properties, items labeled A & C need to be converted to UnaryExpressions
* This essentially changes these to ${-gridSize * 5}px; & ${-gridSize * 8}px respectively - resulting in correct CSS
* generation for these negative values.
*
* Without this manipulation you would end up with:
* -40px for A & B
* -64px for C & D
*
* With this manipulation you end up with:
* -40px for A
* 40px for B
* -64px for C
* 64px for D
*
* @param nodeExpression Node we're interested in manipulating
*/
const convertNegativeCssValuesToUnaryExpression = (nodeExpression: t.Expression): t.Expression => {
if (t.isBinaryExpression(nodeExpression)) {
return {
...nodeExpression,
left: createNegativeUnaryExpression(nodeExpression.left as t.Identifier),
} as t.Expression;
} else if (t.isIdentifier(nodeExpression)) {
return createNegativeUnaryExpression(nodeExpression) as t.Expression;
}
return nodeExpression;
};

/*
* Extracts the keyframes CSS from the `@compiled/react` keyframes usage.
*
Expand Down Expand Up @@ -556,7 +614,7 @@ const extractTemplateLiteral = (node: t.TemplateLiteral, meta: Metadata): CSSOut

// Quasis are the string pieces of the template literal - the parts around the interpolations
const literalResult = node.quasis.reduce<string>((acc, quasi, index): string => {
const nodeExpression = node.expressions[index] as t.Expression | undefined;
let nodeExpression = node.expressions[index] as t.Expression | undefined;

if (
!nodeExpression ||
Expand All @@ -566,6 +624,14 @@ const extractTemplateLiteral = (node: t.TemplateLiteral, meta: Metadata): CSSOut
return acc + quasi.value.raw + suffix;
}

// Deal with any negative values such as:
// margin: -${gridSize}, top: -${gridSize} etc.
if (quasi.value.raw.endsWith('-') && !t.isArrowFunctionExpression(nodeExpression)) {
// Remove the '-' from the quasi, as it will soon be moved to a unary expression
quasi.value.raw = quasi.value.raw.slice(0, -1);
nodeExpression = convertNegativeCssValuesToUnaryExpression(nodeExpression);
}

/**
* Manipulate the node if the CSS property, pseudo classes or pseudo elements are defined
* within a template element rather than in a expression.
Expand Down
20 changes: 20 additions & 0 deletions stories/negative-css-values.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { styled } from '@compiled/react';

export default {
title: 'css/negative css values',
};

const gridSize = 8;

const LayoutRight = styled.aside`
position: relative;
margin-right: -${gridSize * 5}px;
margin-left: ${gridSize * 5}px;
top: -${gridSize}px;
bottom: ${gridSize}px;
left: -6px;
right: 6px;
color: blue;
`;

export const NegativeCssValues = (): JSX.Element => <LayoutRight>Layout Right</LayoutRight>;

0 comments on commit 5e3ad5e

Please sign in to comment.