diff --git a/e2e/rescript-v10-JSX4/src/content_test.res b/e2e/rescript-v10-JSX4/src/content_test.res index 130952f7..2c093274 100644 --- a/e2e/rescript-v10-JSX4/src/content_test.res +++ b/e2e/rescript-v10-JSX4/src/content_test.res @@ -23,11 +23,13 @@ describe("content as string", () => { }) let testData = list{ - (%css("content: ''"), CSS.contentRule(#text("''"))), - (%css("content: '\"'"), CSS.contentRule(#text(`'"'`))), - (%css(`content: '\"'`), CSS.contentRule(#text("'\"'"))), - (%css("content: ' '"), CSS.contentRule(#text("' '"))), - (%css("content: 'single'"), CSS.contentRule(#text("'single'"))), + (%css(`content: ''`), CSS.contentRule(#text("''"))), + (%css(`content: ""`), CSS.contentRule(#text("''"))), + (%css(`content: ' '`), CSS.contentRule(#text("' '"))), + (%css(`content: " "`), CSS.contentRule(#text("' '"))), + (%css(`content: '"'`), CSS.contentRule(#text("\""))), + (%css(`content: "'"`), CSS.contentRule(#text("'"))), + (%css(`content: 'xxx'`), CSS.contentRule(#text(`xxx`))), (%css(`font-family: "Lola"`), CSS.fontFamily("Lola")), (%css(`font-family: "Lola del rio"`), CSS.fontFamily("Lola del rio")), } diff --git a/e2e/rescript-v9-JSX3/src/content_test.res b/e2e/rescript-v9-JSX3/src/content_test.res index 130952f7..2c093274 100644 --- a/e2e/rescript-v9-JSX3/src/content_test.res +++ b/e2e/rescript-v9-JSX3/src/content_test.res @@ -23,11 +23,13 @@ describe("content as string", () => { }) let testData = list{ - (%css("content: ''"), CSS.contentRule(#text("''"))), - (%css("content: '\"'"), CSS.contentRule(#text(`'"'`))), - (%css(`content: '\"'`), CSS.contentRule(#text("'\"'"))), - (%css("content: ' '"), CSS.contentRule(#text("' '"))), - (%css("content: 'single'"), CSS.contentRule(#text("'single'"))), + (%css(`content: ''`), CSS.contentRule(#text("''"))), + (%css(`content: ""`), CSS.contentRule(#text("''"))), + (%css(`content: ' '`), CSS.contentRule(#text("' '"))), + (%css(`content: " "`), CSS.contentRule(#text("' '"))), + (%css(`content: '"'`), CSS.contentRule(#text("\""))), + (%css(`content: "'"`), CSS.contentRule(#text("'"))), + (%css(`content: 'xxx'`), CSS.contentRule(#text(`xxx`))), (%css(`font-family: "Lola"`), CSS.fontFamily("Lola")), (%css(`font-family: "Lola del rio"`), CSS.fontFamily("Lola del rio")), } diff --git a/packages/css-property-parser/test/Standard_test.re b/packages/css-property-parser/test/Standard_test.re index d705ef67..0c351fa3 100644 --- a/packages/css-property-parser/test/Standard_test.re +++ b/packages/css-property-parser/test/Standard_test.re @@ -90,16 +90,12 @@ let tests = [ let pp_length = (ppf, x) => Fmt.pf(ppf, "%S", render_length(x)); let length = Alcotest.testable(pp_length, (==)); let to_check = Alcotest.result(length, Alcotest.string); - check(__POS__, to_check, parse("56cm"), Ok(`Cm(56.))); - check(__POS__, to_check, parse("57px"), Ok(`Px(57.))); - check( - __POS__, - to_check, - parse("59invalid"), - Error("unknown dimension"), - ); - check(__POS__, to_check, parse("0"), Ok(`Zero)); - check(__POS__, to_check, parse("60"), Error("expected length")); + let expect = check(__POS__, to_check); + expect(parse("56cm"), Ok(`Cm(56.))); + expect(parse("57px"), Ok(`Px(57.))); + expect(parse("59invalid"), Error("unknown dimension")); + expect(parse("0"), Ok(`Zero)); + expect(parse("60"), Error("expected length")); }), test("", () => { let parse = parse([%value ""]); @@ -307,13 +303,9 @@ let tests = [ test("", () => { let parse = parse([%value ""]); let to_check = Alcotest.result(Alcotest.string, Alcotest.string); - check(__POS__, to_check, parse("test"), Ok("test")); - check( - __POS__, - to_check, - parse("'ohno'"), - Error("expected an indentifier"), - ); + let expect = check(__POS__, to_check); + expect(parse("test"), Ok("test")); + expect(parse("'ohno'"), Error("expected an indentifier")); }), test("", () => { let parse = parse([%value ""]); @@ -370,34 +362,34 @@ let tests = [ test("", () => { let parse = parse([%value ""]); let to_check = Alcotest.result(Alcotest.string, Alcotest.string); - check(__POS__, to_check, parse("'tuturu'"), Ok("tuturu")); - check(__POS__, to_check, parse("'67.8'"), Ok("67.8")); - check(__POS__, to_check, parse("ident"), Error("expected a string")); - check(__POS__, to_check, parse("68.9"), Error("expected a string")); + let expect = check(__POS__, to_check); + expect(parse({|'tuturu'|}), Ok("tuturu")); + expect(parse({|'67.8'|}), Ok("67.8")); + expect(parse({|ident|}), Error("expected a string")); + expect(parse({|68.9|}), Error("expected a string")); + expect(parse({|"this is a 'string'."|}), Ok("this is a 'string'.")); + expect(parse({|""|}), Ok("")); + expect(parse({|''|}), Ok("")); + expect(parse({|" "|}), Ok(" ")); + expect(parse({|'"'|}), Ok("\"")); + expect(parse({|' '|}), Ok(" ")); + expect(parse({|"this is a \"string\"."|}), Ok("this is a \"string\".")); + expect(parse({|'this is a "string".'|}), Ok("this is a \"string\".")); + expect(parse({|'this is a \'string\'.'|}), Ok("this is a \'string\'.")); }), test("", () => { let parse = parse([%value ""]); let to_check = Alcotest.result(Alcotest.string, Alcotest.string); - check(__POS__, to_check, parse("--random"), Ok("--random")); - check( - __POS__, - to_check, - parse("random'"), - Error("expected a --variable"), - ); + let expect = check(__POS__, to_check); + expect(parse("--random"), Ok("--random")); + expect(parse("random'"), Error("expected a --variable")); }), test("", () => { let parse = parse([%value ""]); let to_check = Alcotest.result(Alcotest.string, Alcotest.string); - check( - __POS__, - to_check, - parse("url(https://google.com)"), - Ok("https://google.com"), - ); - check( - __POS__, - to_check, + let expect = check(__POS__, to_check); + expect(parse("url(https://google.com)"), Ok("https://google.com")); + expect( parse("url(\"https://duckduckgo.com\")"), Ok("https://duckduckgo.com"), ); @@ -406,14 +398,10 @@ let tests = [ test("", () => { let parse = parse([%value ""]); let to_check = Alcotest.result(Alcotest.string, Alcotest.string); - check(__POS__, to_check, parse("#abc"), Ok("abc")); - check(__POS__, to_check, parse("#abcdefgh"), Ok("abcdefgh")); - check( - __POS__, - to_check, - parse("#abcdefghi"), - Error("expected a hex-color"), - ); + let expect = check(__POS__, to_check); + expect(parse("#abc"), Ok("abc")); + expect(parse("#abcdefgh"), Ok("abcdefgh")); + expect(parse("#abcdefghi"), Error("expected a hex-color")); }), test("", () => { let parse = parse([%value ""]); @@ -426,14 +414,10 @@ let tests = [ ), Alcotest.string, ); - check(__POS__, to_check, parse("[abc]"), Ok(((), ["abc"], ()))); - check(__POS__, to_check, parse("[a b]"), Ok(((), ["a", "b"], ()))); - check( - __POS__, - to_check, - parse("[a b c]"), - Ok(((), ["a", "b", "c"], ())), - ); + let expect = check(__POS__, to_check); + expect(parse("[abc]"), Ok(((), ["abc"], ()))); + expect(parse("[a b]"), Ok(((), ["a", "b"], ()))); + expect(parse("[a b c]"), Ok(((), ["a", "b", "c"], ()))); }), test("chars", () => { let parse = parse([%value "? ',' "]); @@ -446,12 +430,8 @@ let tests = [ ), Alcotest.string, ); - check( - __POS__, - to_check, - parse("'lola' , 'flores'"), - Ok((Some("lola"), (), "flores")), - ); + let expect = check(__POS__, to_check); + expect(parse("'lola' , 'flores'"), Ok((Some("lola"), (), "flores"))); }), test("custom-ident vs all", () => { let parse = parse([%value " | 'all'"]); @@ -464,29 +444,19 @@ let tests = [ let pp_output = (ppf, x) => Fmt.pf(ppf, "%S", render_output(x)); let output = Alcotest.testable(pp_output, (==)); let to_check = Alcotest.result(output, Alcotest.string); - check(__POS__, to_check, parse("all"), Ok(`All)); - check(__POS__, to_check, parse("moar"), Ok(`Custom_ident("moar"))); + let expect = check(__POS__, to_check); + expect(parse("all"), Ok(`All)); + expect(parse("moar"), Ok(`Custom_ident("moar"))); }), test("interpolation", () => { let parse = parse([%value ""]); let to_check = Alcotest.result(Alcotest.list(Alcotest.string), Alcotest.string); - check( - __POS__, - to_check, - parse("$(Module.value)"), - Ok(["Module", "value"]), - ); - check( - __POS__, - to_check, - parse("$(Module'.value')"), - Ok(["Module'", "value'"]), - ); + let expect = check(__POS__, to_check); + expect(parse("$(Module.value)"), Ok(["Module", "value"])); + expect(parse("$(Module'.value')"), Ok(["Module'", "value'"])); /* TODO: Add error message into interpolation */ - check( - __POS__, - to_check, + expect( parse("asd"), Error("Expected 'delimiter $' but instead got ident asd"), ); diff --git a/packages/ppx/src/Property_to_runtime.re b/packages/ppx/src/Property_to_runtime.re index 72ec3c9d..8871b71b 100644 --- a/packages/ppx/src/Property_to_runtime.re +++ b/packages/ppx/src/Property_to_runtime.re @@ -2935,11 +2935,11 @@ let transform = let render_origin = (~loc) => fun - | `Center => variant_to_expression(~loc, `Center) - | `Left => variant_to_expression(~loc, `Left) - | `Right => variant_to_expression(~loc, `Right) - | `Bottom => variant_to_expression(~loc, `Bottom) - | `Top => variant_to_expression(~loc, `Top) + | `Center as x + | `Left as x + | `Right as x + | `Top as x + | `Bottom as x => variant_to_expression(~loc, x) | `Function_calc(fc) => render_function_calc(~loc, fc) | `Interpolation(v) => render_variable(~loc, v) | `Length(l) => render_length(~loc, l) @@ -4656,11 +4656,31 @@ let render_quote = (~loc, quote: Types.quote) => { }; }; -/* let render_counter = (~loc, label, style) => { - let labelExpr = render_string(~loc, label); - let styleExpr = render_string(~loc, style); - [%expr `counter(([%e labelExpr], [%e styleExpr]))]; -}; */ +let render_content_string = (~loc, str) => { + let length = String.length(str); + let str = + if (length == 0) { + [%expr {js|''|js}]; + } else if (length == 1 && str.[0] == '"') { + [%expr {js|'"'|js}]; + } else if (length == 1 && str.[0] == ' ') { + [%expr {js|' '|js}]; + } else if (length == 1 && str.[0] == '\'') { + [%expr {js|"'"|js}]; + } else if (length == 2 && str.[0] == '"' && str.[1] == '"') { + [%expr {js|""|js}]; + } else { + let first = str.[0]; + let last = str.[length - 1]; + switch (first, last) { + | ('\'', '\'') => [%expr [%e render_string(~loc, str)]] + | ('"', '"') => [%expr [%e render_string(~loc, str)]] + | _ => + [%expr [%e render_string(~loc, str)]]; + }; + }; + [%expr `text([%e str])]; +}; let render_content_list = (~loc, content_list: Types.content_list) => { content_list @@ -4668,7 +4688,7 @@ let render_content_list = (~loc, content_list: Types.content_list) => { switch (content_item) { | `Contents => [%expr `contents] | `Quote(quote) => render_quote(~loc, quote) - | `String(str) => [%expr `text([%e render_string(~loc, str)])] + | `String(str) => render_content_string(~loc, str) | `Url(u) => render_url(~loc, u) | `Counter(_label, _, _style) => raise(Unsupported_feature) | `Function_attr(_attr) => raise(Unsupported_feature) @@ -4683,7 +4703,7 @@ let content = | `Normal => [[%expr CSS.contentRule(`normal)]] | `None => [[%expr CSS.contentRule(`none)]] | `String(str) => [ - [%expr CSS.contentRule(`text([%e render_string(~loc, str)]))], + [%expr CSS.contentRule([%e render_content_string(~loc, str)])], ] | `Interpolation(v) => [ [%expr CSS.contentRule([%e render_variable(~loc, v)])], diff --git a/packages/ppx/test/css-support/content.t/input.re b/packages/ppx/test/css-support/content.t/input.re index 984f8d62..a596c5e3 100644 --- a/packages/ppx/test/css-support/content.t/input.re +++ b/packages/ppx/test/css-support/content.t/input.re @@ -49,3 +49,15 @@ [%css {|content: revert;|}]; [%css {|content: revert-layer;|}]; [%css {|content: unset;|}]; + +[%css "content: '点';"]; +[%css {|content: '点';|}]; +[%css {|content: "点";|}]; +[%css {|content: "lola";|}]; +[%css {|content: 'lola';|}]; +[%css {|content: "";|}]; +[%css {|content: " ";|}]; +[%css {|content: ' ';|}]; +[%css {|content: '';|}]; +[%css {|content: "'";|}]; +[%css {|content: '"';|}]; diff --git a/packages/ppx/test/css-support/content.t/run.t b/packages/ppx/test/css-support/content.t/run.t index 13db8519..a858673e 100644 --- a/packages/ppx/test/css-support/content.t/run.t +++ b/packages/ppx/test/css-support/content.t/run.t @@ -35,7 +35,7 @@ If this test fail means that the module is not in sync with the ppx ]; CSS.unsafe({js|quotes|js}, {js|auto|js}); CSS.contentsRule([|`text({js|►|js})|], Some({js||js})); - CSS.contentsRule([|`text({js||js})|], None); + CSS.contentsRule([|`text({js|''|js})|], None); CSS.unsafe({js|content|js}, {js|unset|js}); CSS.contentRule(`normal); CSS.contentRule(`none); @@ -79,3 +79,13 @@ If this test fail means that the module is not in sync with the ppx CSS.unsafe({js|content|js}, {js|revert|js}); CSS.unsafe({js|content|js}, {js|revert-layer|js}); CSS.unsafe({js|content|js}, {js|unset|js}); + CSS.contentsRule([|`text({js|点|js})|], None); + CSS.contentsRule([|`text({js|点|js})|], None); + CSS.contentsRule([|`text({js|点|js})|], None); + CSS.contentsRule([|`text({js|lola|js})|], None); + CSS.contentsRule([|`text({js|''|js})|], None); + CSS.contentsRule([|`text({js|' '|js})|], None); + CSS.contentsRule([|`text({js|' '|js})|], None); + CSS.contentsRule([|`text({js|''|js})|], None); + CSS.contentsRule([|`text({js|"'"|js})|], None); + CSS.contentsRule([|`text({js|'"'|js})|], None); diff --git a/packages/ppx/test/native/Selector_test.re b/packages/ppx/test/native/Selector_test.re index e1fae5c9..72931367 100644 --- a/packages/ppx/test/native/Selector_test.re +++ b/packages/ppx/test/native/Selector_test.re @@ -5,27 +5,10 @@ let loc = Location.none; let simple_tests = [ ( - ":before { content: '点'; }", - [%expr [%cx ":before { content: '点'; }"]], + ":before { display: none; }", + [%expr [%cx ":before { display: none; }"]], [%expr - CSS.style([| - CSS.selector( - {js|:before|js}, - [|CSS.contentsRule([|`text({js|点|js})|], None)|], - ), - |]) - ], - ), - ( - ":before { content: '•'; }", - [%expr [%cx ":before { content: '•'; }"]], - [%expr - CSS.style([| - CSS.selector( - {js|:before|js}, - [|CSS.contentsRule([|`text({js|•|js})|], None)|], - ), - |]) + CSS.style([|CSS.selector({js|:before|js}, [|CSS.display(`none)|])|]) ], ), ( diff --git a/packages/runtime/native/shared/Css_types.ml b/packages/runtime/native/shared/Css_types.ml index 42f74f27..dab2957f 100644 --- a/packages/runtime/native/shared/Css_types.ml +++ b/packages/runtime/native/shared/Css_types.ml @@ -2869,16 +2869,18 @@ module Content = struct let text_to_string value = if Kloth.String.length value = 0 then {js|''|js} (* value = "" -> '' *) + else if Kloth.String.length value = 1 && Kloth.String.get value 0 = '"' then + {js|'"'|js} + else if Kloth.String.length value = 1 && Kloth.String.get value 0 = '\'' + then {js|"'"|js} else if Kloth.String.length value = 2 && Kloth.String.get value 0 = '"' && Kloth.String.get value 1 = '"' then {js|''|js} else ( - match Kloth.String.get value 0, Kloth.String.length value with - | '\'', 1 -> {js|"'"|js} - | '"', 1 -> {js|'"'|js} - | '\'', _ | '"', _ -> value + match Kloth.String.get value 0 with + | '\'' | '"' -> value | _ -> {js|"|js} ^ value ^ {js|"|js}) let toString x = diff --git a/packages/runtime/test/test_styles.ml b/packages/runtime/test/test_styles.ml index 6f9dc661..31d8d4de 100644 --- a/packages/runtime/test/test_styles.ml +++ b/packages/runtime/test/test_styles.ml @@ -450,7 +450,7 @@ let multiple_pseudo = let css = get_string_style_rules () in assert_string css (Printf.sprintf - ".%s { padding: 0; } .%s::before { content: \" \"; -webkit-transform: \ + ".%s { padding: 0; } .%s::before { content: ' '; -webkit-transform: \ translateX(-50%%); -moz-transform: translateX(-50%%); -ms-transform: \ translateX(-50%%); transform: translateX(-50%%); } .%s::after { \ content: \" \"; -webkit-transform: translateX(-50%%); -moz-transform: \