Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 45 additions & 21 deletions packages/ppx/src/Css_to_runtime.re
Original file line number Diff line number Diff line change
Expand Up @@ -421,36 +421,59 @@ and render_selectors = (~loc, selectors) => {
selectors
|> List.map(((selector, _loc)) => render_selector(~loc, selector));
}
and render_style_rule = (~loc, rule: style_rule) => {
let (prelude, prelude_loc) = rule.prelude;
and render_style_rule = (~loc, ~ignore_first_level=false, rule: style_rule) => {
let starts_with_double_dot = selector =>
String.starts_with(~prefix=":", selector);
let contains_ampersand = selector => String.contains(selector, '&');

let (prelude_ast, prelude_ast_loc) = rule.prelude;
let selector_location =
Styled_ppx_css_parser.Parser_location.intersection(loc, prelude_loc);
Styled_ppx_css_parser.Parser_location.update_pos_lnum(
prelude_ast_loc,
loc,
);

let selector_expr =
let declarations =
render_declarations(~loc, rule.block)
|> Builder.pexp_array(~loc=selector_location);

let (delimiter, attrs) =
Platform_attributes.string_delimiter(~loc=selector_location);

let selector_name =
prelude
let prelude =
prelude_ast
|> render_selectors(~loc=selector_location)
|> List.map(String.trim)
|> List.map(
String_interpolation.transform(
~attrs,
~delimiter,
~loc=selector_location,
),
)
|> Builder.pexp_array(~loc=selector_location);
|> List.map(String.trim);

Helper.Exp.apply(
~loc=selector_location,
CSS.selectorMany(~loc=selector_location),
[(Nolabel, selector_name), (Nolabel, selector_expr)],
);
let starts_with_double_dot_and_no_ampersand =
List.exists(
selector => {
starts_with_double_dot(selector) && !contains_ampersand(selector)
},
prelude,
);
if (!ignore_first_level && starts_with_double_dot_and_no_ampersand) {
Error.expr(
~loc=selector_location,
"Ampersand is needed if selector begins with pseudo-class or pseudo-element.",
);
} else {
let prelude_transformed =
prelude
|> List.map(
String_interpolation.transform(
~attrs,
~delimiter,
~loc=selector_location,
),
)
|> Builder.pexp_array(~loc=selector_location);
Helper.Exp.apply(
~loc=selector_location,
CSS.selectorMany(~loc=selector_location),
[(Nolabel, prelude_transformed), (Nolabel, declarations)],
);
};
};

let addLabel = (~loc, label, emotionExprs) => [
Expand Down Expand Up @@ -552,7 +575,8 @@ If your intent is to apply the declaration to all elements, use the universal se
ruleList
|> List.map(rule => {
switch (rule) {
| Style_rule(style_rule) => render_style_rule(~loc, style_rule)
| Style_rule(style_rule) =>
render_style_rule(~loc, ~ignore_first_level=true, style_rule)
| At_rule(at_rule) => render_at_rule(~loc, at_rule)
| _ =>
Error.expr(~loc=stylesheet_loc, onlyStyleRulesAndAtRulesSupported)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
let selectors = [%cx
{|
:hover {
color: red;
}

::first-line {
color: red;
}
|}
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
This test ensures the ppx generates the correct output against styled-ppx.native
If this test fail means that the module is not in sync with the ppx

$ cat > dune-project << EOF
> (lang dune 3.10)
> EOF

$ cat > dune << EOF
> (executable
> (name input)
> (libraries styled-ppx.native)
> (preprocess (pps styled-ppx)))
> EOF

$ dune build
File "input.re", lines 2-3, characters 0-9:
Error: Ampersand is needed if selector begins with pseudo-class or
pseudo-element.
[1]

$ dune describe pp ./input.re | sed '1,/^];$/d'

let selectors =
CSS.style([|
CSS.label("selectors"),
[%ocaml.error
"Ampersand is needed if selector begins with pseudo-class or pseudo-element."
],
[%ocaml.error
"Ampersand is needed if selector begins with pseudo-class or pseudo-element."
],
|]);
4 changes: 2 additions & 2 deletions packages/ppx/test/css-support/selectors.t/input.re
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ let _chart = [%cx

.recharts-cartesian-grid-horizontal {
line {
:nth-last-child(1), :nth-last-child(2) {
&:nth-last-child(1), &:nth-last-child(2) {
stroke-opacity: 0;
}
}
Expand All @@ -15,7 +15,7 @@ let _chart = [%cx
.recharts-scatter .recharts-scatter-symbol .recharts-symbols {
opacity: 0.8;

:hover {
&:hover {
opacity: 1;
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/ppx/test/css-support/selectors.t/run.t
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ If this test fail means that the module is not in sync with the ppx
[|{js|line|js}|],
[|
CSS.selectorMany(
[|{js|:nth-last-child(1)|js}, {js|:nth-last-child(2)|js}|],
[|{js|&:nth-last-child(1)|js}, {js|&:nth-last-child(2)|js}|],
[|CSS.SVG.strokeOpacity(`num(0.))|],
),
|],
Expand All @@ -38,7 +38,7 @@ If this test fail means that the module is not in sync with the ppx
|],
[|
CSS.opacity(0.8),
CSS.selectorMany([|{js|:hover|js}|], [|CSS.opacity(1.)|]),
CSS.selectorMany([|{js|&:hover|js}|], [|CSS.opacity(1.)|]),
|],
),
|]);
Expand Down
12 changes: 6 additions & 6 deletions packages/ppx/test/native/Selector_test.re
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ let loc = Location.none;

let simple_tests = [
(
":before { display: none; }",
[%expr [%cx ":before { display: none; }"]],
"&:before { display: none; }",
[%expr [%cx "&:before { display: none; }"]],
[%expr
CSS.style([|
CSS.selectorMany([|{js|:before|js}|], [|CSS.display(`none)|]),
CSS.selectorMany([|{js|&:before|js}|], [|CSS.display(`none)|]),
|])
],
),
Expand Down Expand Up @@ -465,12 +465,12 @@ let complex_tests = [
],
),
(
":is(h1, $(gap), h3):has(+ :is(h2, h3, h4))",
[%expr [%cx {| :is(h1, $(gap), h3):has(+ :is(h2, h3, h4)) {} |}]],
"&:is(h1, $(gap), h3):has(+ :is(h2, h3, h4))",
[%expr [%cx {| &:is(h1, $(gap), h3):has(+ :is(h2, h3, h4)) {} |}]],
[%expr
CSS.style([|
CSS.selectorMany(
[|{js|:is(h1, |js} ++ gap ++ {js|, h3):has(+ :is(h2, h3, h4))|js}|],
[|{js|&:is(h1, |js} ++ gap ++ {js|, h3):has(+ :is(h2, h3, h4))|js}|],
[||],
),
|])
Expand Down
50 changes: 25 additions & 25 deletions packages/runtime/test/test_styles.ml
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@ let avoid_hash_collision =
{|
color: $(color);

:disabled {
&:disabled {
color: $(disabledColor);
}

:hover {
&:hover {
color: $(hoverColor);
}
|}]
Expand All @@ -93,8 +93,8 @@ let avoid_hash_collision =
{|
display: flex;

:before,
:after {
&:before,
&:after {
content: '';
flex: 0 0 $(padding);
}
Expand All @@ -106,8 +106,8 @@ let avoid_hash_collision =
{|
display: flex;

:before,
:after {
&:before,
&:after {
content: '';
flex: 0 1 $(padding);
}
Expand Down Expand Up @@ -288,7 +288,7 @@ let ampersand_everywhere_2 =
display: none;
}
}
:first-child {
&:first-child {
.felipe & {
display: none;
}
Expand Down Expand Up @@ -316,7 +316,7 @@ let ampersand_everywhere_3 =
padding: 0;
list-style-type: none;

:first-child {
&:first-child {
.$(hasTwoColumnList) & {
@media $(desktopDown) {
padding-bottom: $(px16);
Expand All @@ -342,9 +342,9 @@ let pseudo_selectors_everywhere =
{|
display: block;

::before {
&::before {
display: none;
::after {
&::after {
display: none;
}
}
Expand Down Expand Up @@ -394,7 +394,7 @@ let selector_nested_with_mq_and_declarations =
li {
list-style-type: none;

::before {
&::before {
position: absolute;
left: -20px;
content: "✓";
Expand Down Expand Up @@ -581,11 +581,11 @@ let functional_pseudo =
[%cx
{|
.foo, .bar {
:is(ol, ul, menu:unsupported) :is(ol, ul) {
&:is(ol, ul, menu:unsupported) :is(ol, ul) {
color: green;
}

:is(ol, ul) :is(ol, ul) ol {
&:is(ol, ul) :is(ol, ul) ol {
list-style-type: lower-greek;
color: chocolate;
}
Expand All @@ -598,16 +598,16 @@ let functional_pseudo =
color: darkmagenta;
}

:where(ol, ul, menu:unsupported) :where(ol, ul) {
&:where(ol, ul, menu:unsupported) :where(ol, ul) {
color: green;
}

:where(ol, ul) :where(ol, ul) ol {
&:where(ol, ul) :where(ol, ul) ol {
list-style-type: lower-greek;
color: chocolate;
}

:is(h1, h2, h3):has(+ :is(h2, h3, h4)) {
&:is(h1, h2, h3):has(+ :is(h2, h3, h4)) {
margin: 0 0 0.25rem 0;
}

Expand Down Expand Up @@ -746,8 +746,8 @@ let selector_nested_with_pseudo_2 =
[%cx
{|
position: relative;
:hover {
::after {
&:hover {
&::after {
top: 50px;
}
}
Expand Down Expand Up @@ -1125,19 +1125,19 @@ let pseudo_selectors =
padding-bottom: 9px;
border-radius: 0;

::placeholder {
&::placeholder {
color: currentColor;
}

:hover {
&:hover {
border: 1px solid transparent;
}

:focus {
&:focus {
outline: none;
}

:disabled {
&:disabled {
color: transparent;
}|}]
in
Expand All @@ -1161,8 +1161,8 @@ let real_world =
&.$(buttonActive) {
margin: 0px;

::before,
::after {
&::before,
&::after {
top: 40px;
}
}
Expand Down Expand Up @@ -1215,7 +1215,7 @@ let global_with_selector =
test "global_with_selector" @@ fun () ->
[%global
{| html { line-height: 1.15; }
a { :hover { padding: 0; } }
a { &:hover { padding: 0; } }
|}];
let css = get_string_style_rules () in
assert_string css
Expand Down
Loading