From f8fb50f09ef8bc720f8fdab13991e116e50238d6 Mon Sep 17 00:00:00 2001 From: Gusztav Szikszai Date: Wed, 25 Sep 2019 09:49:46 +0200 Subject: [PATCH 01/21] Start refactoring styling (parsing). - Allow selectors in selectors and media queries - Don't require ampersend for sub selectors --- spec/formatters/css_with_comments | 2 +- spec/formatters/css_with_selector | 2 +- spec/parsers/css_definition_spec.cr | 1 - spec/parsers/css_selector_spec.cr | 14 ++---- src/ast/css_media.cr | 5 +- src/ast/css_selector.cr | 7 ++- src/ast/style.cr | 7 +-- src/compilers/style.cr | 43 +++++++++++++++-- src/formatters/css_media.cr | 5 +- src/formatters/css_selector.cr | 7 +-- src/formatters/style.cr | 5 +- src/messages/css_definition_expected_colon.cr | 13 ----- src/messages/css_selector_expected_name.cr | 11 ----- .../css_selector_space_after_ampersand.cr | 13 ----- src/parsers/css_definition.cr | 3 +- src/parsers/css_media.cr | 19 ++------ src/parsers/css_selector.cr | 47 ++++++------------- src/parsers/style.cr | 33 ++++--------- src/type_checkers/css_media.cr | 2 +- src/type_checkers/css_selector.cr | 2 +- src/type_checkers/style.cr | 4 +- 21 files changed, 87 insertions(+), 158 deletions(-) delete mode 100644 src/messages/css_definition_expected_colon.cr delete mode 100644 src/messages/css_selector_expected_name.cr delete mode 100644 src/messages/css_selector_space_after_ampersand.cr diff --git a/spec/formatters/css_with_comments b/spec/formatters/css_with_comments index f548d3d1c..f774eb2c2 100644 --- a/spec/formatters/css_with_comments +++ b/spec/formatters/css_with_comments @@ -26,7 +26,7 @@ component A { color: blue; /* C */ - & selector { + selector { /* D */ x: y; diff --git a/spec/formatters/css_with_selector b/spec/formatters/css_with_selector index 72fa0fa6c..754f7643b 100644 --- a/spec/formatters/css_with_selector +++ b/spec/formatters/css_with_selector @@ -12,7 +12,7 @@ component A { -------------------------------------------------------------------------------- component A { style test { - & div { + div { color: red; } diff --git a/spec/parsers/css_definition_spec.cr b/spec/parsers/css_definition_spec.cr index 15fb52598..3e7cb1314 100644 --- a/spec/parsers/css_definition_spec.cr +++ b/spec/parsers/css_definition_spec.cr @@ -7,7 +7,6 @@ describe "Css Definition" do expect_ignore "A" expect_ignore ":" - expect_error "a", Mint::Parser::CssDefinitionExpectedColon expect_error "a:", Mint::Parser::CssDefinitionExpectedSemicolon expect_error "a: b", Mint::Parser::CssDefinitionExpectedSemicolon expect_error "a: {", Mint::Parser::CssInterpolationExpectedExpression diff --git a/spec/parsers/css_selector_spec.cr b/spec/parsers/css_selector_spec.cr index d0847a8ef..04357a565 100644 --- a/spec/parsers/css_selector_spec.cr +++ b/spec/parsers/css_selector_spec.cr @@ -3,14 +3,10 @@ require "../spec_helper" describe "Css Selectors" do subject css_selector - expect_error "&v", Mint::Parser::CssSelectorSpaceAfterAmpersand - expect_error "& ", Mint::Parser::CssSelectorExpectedName - expect_error "& v", Mint::Parser::CssSelectorExpectedOpeningBracket - expect_error "& v ", Mint::Parser::CssSelectorExpectedOpeningBracket - expect_error "& v {", Mint::Parser::CssSelectorExpectedClosingBracket - expect_error "& v { ", Mint::Parser::CssSelectorExpectedClosingBracket - expect_error "& v { a: b;", Mint::Parser::CssSelectorExpectedClosingBracket + expect_error "v {", Mint::Parser::CssSelectorExpectedClosingBracket + expect_error "v { ", Mint::Parser::CssSelectorExpectedClosingBracket + expect_error "v { a: b;", Mint::Parser::CssSelectorExpectedClosingBracket - expect_ok "& valami { }" - expect_ok "& valami { a: b; }" + expect_ok "valami { }" + expect_ok "valami { a: b; }" end diff --git a/src/ast/css_media.cr b/src/ast/css_media.cr index e4690dcf8..b5c56c014 100644 --- a/src/ast/css_media.cr +++ b/src/ast/css_media.cr @@ -1,10 +1,9 @@ module Mint class Ast class CssMedia < Node - getter content, definitions, comments + getter content, body - def initialize(@definitions : Array(CssDefinition), - @comments : Array(Comment), + def initialize(@body : Array(Node), @content : String, @input : Data, @from : Int32, diff --git a/src/ast/css_selector.cr b/src/ast/css_selector.cr index 82877f414..af7033434 100644 --- a/src/ast/css_selector.cr +++ b/src/ast/css_selector.cr @@ -1,11 +1,10 @@ module Mint class Ast class CssSelector < Node - getter selectors, definitions, comments + getter selectors, body - def initialize(@definitions : Array(CssDefinition), - @comments : Array(Comment), - @selectors : Array(String), + def initialize(@selectors : Array(String), + @body : Array(Node), @input : Data, @from : Int32, @to : Int32) diff --git a/src/ast/style.cr b/src/ast/style.cr index 5ce5ff66c..bb03468c2 100644 --- a/src/ast/style.cr +++ b/src/ast/style.cr @@ -1,12 +1,9 @@ module Mint class Ast class Style < Node - getter name, definitions, selectors, medias, comments + getter name, body - def initialize(@definitions : Array(CssDefinition), - @selectors : Array(CssSelector), - @comments : Array(Comment), - @medias : Array(CssMedia), + def initialize(@body : Array(Node), @name : Variable, @input : Data, @from : Int32, diff --git a/src/compilers/style.cr b/src/compilers/style.cr index 60ba20859..0aa846190 100644 --- a/src/compilers/style.cr +++ b/src/compilers/style.cr @@ -18,15 +18,48 @@ module Mint prefix = js.style_of(node) - compile prefix, prefix, node.definitions + definitions = [] of Ast::CssDefinition + selectors = [] of Ast::CssSelector + medias = [] of Ast::CssMedia - node.medias.each_with_index do |item, index| - compile prefix + "_media_#{index}", prefix, item.definitions, item.content + node.body.each do |item| + case item + when Ast::CssDefinition + definitions << item + when Ast::CssSelector + selectors << item + when Ast::CssMedia + medias << item + end end - node.selectors.each do |item| + compile prefix, prefix, definitions + + medias.each_with_index do |item, index| + defs = [] of Ast::CssDefinition + + item.body.each do |part| + case part + when Ast::CssDefinition + defs << part + end + end + + compile prefix + "_media_#{index}", prefix, defs, item.content + end + + selectors.each do |item| item.selectors.each do |selector| - compile prefix + selector, prefix, item.definitions + defs = [] of Ast::CssDefinition + + item.body.each do |part| + case part + when Ast::CssDefinition + defs << part + end + end + + compile prefix + selector, prefix, defs end end end diff --git a/src/formatters/css_media.cr b/src/formatters/css_media.cr index 77ccc111b..5f9226ae5 100644 --- a/src/formatters/css_media.cr +++ b/src/formatters/css_media.cr @@ -1,11 +1,8 @@ module Mint class Formatter def format(node : Ast::CssMedia) : String - items = - node.definitions + node.comments - body = - list items + list node.body "@media #{node.content.strip} {\n#{indent(body)}\n}" end diff --git a/src/formatters/css_selector.cr b/src/formatters/css_selector.cr index 5d5facee1..b0f64aa9b 100644 --- a/src/formatters/css_selector.cr +++ b/src/formatters/css_selector.cr @@ -4,14 +4,11 @@ module Mint selectors = node .selectors - .map { |item| "&#{item}" } + .map { |item| item.starts_with?(" ") ? item.lstrip : "&#{item}" } .join(",\n") - items = - node.definitions + node.comments - body = - list items + list node.body "#{selectors} {\n#{indent(body)}\n}" end diff --git a/src/formatters/style.cr b/src/formatters/style.cr index 3e22a0867..0e638bf14 100644 --- a/src/formatters/style.cr +++ b/src/formatters/style.cr @@ -1,14 +1,11 @@ module Mint class Formatter def format(node : Ast::Style) : String - items = - node.definitions + node.selectors + node.medias + node.comments - name = format node.name body = - list items + list node.body "style #{name} {\n#{indent(body)}\n}" end diff --git a/src/messages/css_definition_expected_colon.cr b/src/messages/css_definition_expected_colon.cr deleted file mode 100644 index f14cfb5a9..000000000 --- a/src/messages/css_definition_expected_colon.cr +++ /dev/null @@ -1,13 +0,0 @@ -message CssDefinitionExpectedColon do - title "Syntax Error" - - block do - text "A CSS property and its value must be separated by a" - bold "colon" - code ":" - end - - was_looking_for "colon", got, ":" - - snippet node -end diff --git a/src/messages/css_selector_expected_name.cr b/src/messages/css_selector_expected_name.cr deleted file mode 100644 index 2f70fb871..000000000 --- a/src/messages/css_selector_expected_name.cr +++ /dev/null @@ -1,11 +0,0 @@ -message CssSelectorExpectedName do - title "Syntax Error" - - block do - text "A sub selector must define a selector after the ampersand." - end - - was_looking_for "selector", got - - snippet node -end diff --git a/src/messages/css_selector_space_after_ampersand.cr b/src/messages/css_selector_space_after_ampersand.cr deleted file mode 100644 index bd2b010b2..000000000 --- a/src/messages/css_selector_space_after_ampersand.cr +++ /dev/null @@ -1,13 +0,0 @@ -message CssSelectorSpaceAfterAmpersand do - title "Syntax Error" - - block do - text "There must be a space between the ampersand" - code "&" - text "and the selector, if it's not a pseudo one." - end - - was_looking_for "space", got - - snippet node -end diff --git a/src/parsers/css_definition.cr b/src/parsers/css_definition.cr index f52e1b9c1..4e93a57e4 100644 --- a/src/parsers/css_definition.cr +++ b/src/parsers/css_definition.cr @@ -1,7 +1,6 @@ module Mint class Parser syntax_error CssDefinitionExpectedSemicolon - syntax_error CssDefinitionExpectedColon def css_definition : Ast::CssDefinition | Nil start do |start_position| @@ -12,7 +11,7 @@ module Mint chars "a-zA-Z-" end - char ':', CssDefinitionExpectedColon + skip unless char!(':') whitespace diff --git a/src/parsers/css_media.cr b/src/parsers/css_media.cr index dbe77c99e..1832c7d66 100644 --- a/src/parsers/css_media.cr +++ b/src/parsers/css_media.cr @@ -19,28 +19,15 @@ module Mint body = block( opening_bracket: CssMediaExpectedOpeningBracket, closing_bracket: CssMediaExpectedClosingBracket) do - many { css_definition || comment }.compact - end - - definitions = [] of Ast::CssDefinition - comments = [] of Ast::Comment - - body.each do |item| - case item - when Ast::CssDefinition - definitions << item - when Ast::Comment - comments << item - end + css_body end Ast::CssMedia.new( - definitions: definitions, from: start_position, - comments: comments, content: content, to: position, - input: data) + input: data, + body: body) end end end diff --git a/src/parsers/css_selector.cr b/src/parsers/css_selector.cr index 00a469194..d32e0032d 100644 --- a/src/parsers/css_selector.cr +++ b/src/parsers/css_selector.cr @@ -2,62 +2,43 @@ module Mint class Parser syntax_error CssSelectorExpectedOpeningBracket syntax_error CssSelectorExpectedClosingBracket - syntax_error CssSelectorExpectedName - - syntax_error CssSelectorSpaceAfterAmpersand def css_selector : Ast::CssSelector | Nil start do |start_position| - skip unless char == '&' - selectors = list( terminator: '{', separator: ',' - ) { css_selector_name }.compact + ) { css_selector_name }.reject(&.empty?) + + skip unless selectors.any? + skip unless char == '{' body = block( opening_bracket: CssSelectorExpectedOpeningBracket, closing_bracket: CssSelectorExpectedClosingBracket) do - many { css_definition || comment }.compact - end - - definitions = [] of Ast::CssDefinition - comments = [] of Ast::Comment - - body.each do |item| - case item - when Ast::CssDefinition - definitions << item - when Ast::Comment - comments << item - end + css_body end Ast::CssSelector.new( - definitions: definitions, selectors: selectors, from: start_position, - comments: comments, to: position, - input: data) + input: data, + body: body) end end def css_selector_name : String | Nil - return unless char! '&' - - colon = char!(':') - double_colon = keyword("::") + colon = nil + double_colon = nil - if !colon && !double_colon - whitespace! CssSelectorSpaceAfterAmpersand + if char! '&' + colon = char!(':') + double_colon = keyword("::") end - name = gather { chars "^,{" } - - raise CssSelectorExpectedName unless name - - name = name.strip + name = + gather { chars "^,{}" }.to_s.strip if colon ":#{name}" diff --git a/src/parsers/style.cr b/src/parsers/style.cr index 97cafb263..b366f86ce 100644 --- a/src/parsers/style.cr +++ b/src/parsers/style.cr @@ -16,37 +16,24 @@ module Mint opening_bracket: StyleExpectedOpeningBracket, closing_bracket: StyleExpectedClosingBracket ) do - many { css_definition || css_selector || css_media || comment }.compact - end - - definitions = [] of Ast::CssDefinition - selectors = [] of Ast::CssSelector - comments = [] of Ast::Comment - medias = [] of Ast::CssMedia - - body.each do |item| - case item - when Ast::CssDefinition - definitions << item - when Ast::CssSelector - selectors << item - when Ast::CssMedia - medias << item - when Ast::Comment - comments << item - end + css_body_with_media end Ast::Style.new( - definitions: definitions, - selectors: selectors, from: start_position, - comments: comments, - medias: medias, to: position, input: data, + body: body, name: name) end end + + def css_body_with_media + many { comment || css_definition || css_media || css_selector }.compact + end + + def css_body + many { comment || css_definition || css_selector }.compact + end end end diff --git a/src/type_checkers/css_media.cr b/src/type_checkers/css_media.cr index 8c41cccf9..7d7b82d33 100644 --- a/src/type_checkers/css_media.cr +++ b/src/type_checkers/css_media.cr @@ -1,7 +1,7 @@ module Mint class TypeChecker def check(node : Ast::CssMedia) : Checkable - resolve node.definitions + resolve node.body NEVER end diff --git a/src/type_checkers/css_selector.cr b/src/type_checkers/css_selector.cr index d8baae2e9..b531a363f 100644 --- a/src/type_checkers/css_selector.cr +++ b/src/type_checkers/css_selector.cr @@ -1,7 +1,7 @@ module Mint class TypeChecker def check(node : Ast::CssSelector) : Checkable - resolve node.definitions + resolve node.body NEVER end diff --git a/src/type_checkers/style.cr b/src/type_checkers/style.cr index a7f19f605..3f1298cc6 100644 --- a/src/type_checkers/style.cr +++ b/src/type_checkers/style.cr @@ -1,9 +1,7 @@ module Mint class TypeChecker def check(node : Ast::Style) : Checkable - resolve node.definitions - resolve node.selectors - resolve node.medias + resolve node.body NEVER end From 512554c501f8403c12364ba8ba966e570e095f74 Mon Sep 17 00:00:00 2001 From: Gusztav Szikszai Date: Wed, 25 Sep 2019 10:31:48 +0200 Subject: [PATCH 02/21] Started style builder. --- src/style_builder.cr | 68 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 src/style_builder.cr diff --git a/src/style_builder.cr b/src/style_builder.cr new file mode 100644 index 000000000..6b3bff51d --- /dev/null +++ b/src/style_builder.cr @@ -0,0 +1,68 @@ +module Mint + class StyleBuilder + class Selector + property definitions : Hash(String, Ast::CssInterpolation) = {} of String => String | Ast::CssInterpolation + property medias : Hash(String, Selector) = [] of Selector + property selectors : Array(Selector) = [] of Selector + property name : String + + def initalize(@name) + end + + def compile(prefix) + "" + end + end + + def initialize + @js = JS.new + end + + def build(node : Ast::Style) + selector = Selector.new(js.style_of(node)) + + node.body.each do |item| + case item + when Ast::CssDefinition + selector.definitions[item.name] = item.value + when Ast::CssSelector + selector.selectors << build(item) + when Ast::Media + selector.medias << build(item) + end + end + + selector + end + + def build(node : Ast::CssMedia) + selector = Selector.new(content) + + node.body.each do |item| + case item + when Ast::CssDefinition + selector.definitions[item.name] = item.value + when Ast::CssSelector + selector.selectors << build(item) + end + end + + selector + end + + def build(node : Ast::CssSelector, prefix : String) + selector = Selector.new(node.selectors.join(", ")) + + node.body.each do |item| + case item + when Ast::CssDefinition + selector.definitions[item.name] = item.value + when Ast::CssSelector + selector.selectors << build(item) + end + end + + selector + end + end +end From 7605cec3d053141710d77b450ea79ad0b8cdad61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guszt=C3=A1v=20Szikszai?= Date: Wed, 16 Oct 2019 11:06:20 +0200 Subject: [PATCH 03/21] Implement proper style builder. - Allow infinite nesting of CSS blocks (media queries, selectors) - Implement algorithm for processing CSS blocks - Implement compiler for processed CSS blocks --- spec/style_builder_spec.cr | 62 ++++++++++++++ src/all.cr | 2 + src/parsers/css_media.cr | 4 +- src/parsers/style.cr | 8 +- src/style_builder.cr | 161 ++++++++++++++++++++++++++++--------- 5 files changed, 190 insertions(+), 47 deletions(-) create mode 100644 spec/style_builder_spec.cr diff --git a/spec/style_builder_spec.cr b/spec/style_builder_spec.cr new file mode 100644 index 000000000..07e08410c --- /dev/null +++ b/spec/style_builder_spec.cr @@ -0,0 +1,62 @@ +require "./spec_helper" + +describe Mint::StyleBuilder do + it "builds simple styles" do + example = + <<-MINT + style test { + div, p { + background: red; + + span, strong { + pre { + color: {"red"}; + } + } + + span, strong { + pre { + background: white; + + @media (screen) { + color: blue; + + a { + border: 1px solid red; + } + } + } + } + } + + @media (screen) { + div, p { + font-size: 30px; + } + } + + @media (screen) { + div, p { + color: blue; + } + + @media (print) { + div, p { + color: black; + border-radius: {10}px; + } + } + } + } + MINT + + parser = + Mint::Parser.new(example.strip, "test.mint") + + style = + parser.style.not_nil! + + builder = Mint::StyleBuilder.new + builder.process(style) + end +end diff --git a/src/all.cr b/src/all.cr index c33144f78..5fcef0e03 100644 --- a/src/all.cr +++ b/src/all.cr @@ -32,6 +32,8 @@ require "./ast/node" require "./ast/**" require "./ast" +require "./style_builder" + require "./type_checkers/**" require "./type_checker" diff --git a/src/parsers/css_media.cr b/src/parsers/css_media.cr index 1832c7d66..8b0c82040 100644 --- a/src/parsers/css_media.cr +++ b/src/parsers/css_media.cr @@ -12,9 +12,9 @@ module Mint whitespace! CssMediaExpectedSpaceAfterKeyword - content = gather { chars "^{" }.to_s + content = gather { chars "^{" }.to_s.strip - raise CssMediaExpectedName if content.strip.empty? + raise CssMediaExpectedName if content.empty? body = block( opening_bracket: CssMediaExpectedOpeningBracket, diff --git a/src/parsers/style.cr b/src/parsers/style.cr index b366f86ce..8167dfef2 100644 --- a/src/parsers/style.cr +++ b/src/parsers/style.cr @@ -16,7 +16,7 @@ module Mint opening_bracket: StyleExpectedOpeningBracket, closing_bracket: StyleExpectedClosingBracket ) do - css_body_with_media + css_body end Ast::Style.new( @@ -28,12 +28,8 @@ module Mint end end - def css_body_with_media - many { comment || css_definition || css_media || css_selector }.compact - end - def css_body - many { comment || css_definition || css_selector }.compact + many { comment || css_definition || css_media || css_selector }.compact end end end diff --git a/src/style_builder.cr b/src/style_builder.cr index 6b3bff51d..dfd03c3e3 100644 --- a/src/style_builder.cr +++ b/src/style_builder.cr @@ -1,68 +1,151 @@ module Mint - class StyleBuilder - class Selector - property definitions : Hash(String, Ast::CssInterpolation) = {} of String => String | Ast::CssInterpolation - property medias : Hash(String, Selector) = [] of Selector - property selectors : Array(Selector) = [] of Selector - property name : String + # This is a name pool. It returns a unique identifier for a given item of a + # given base item. + # + # In Mint it's used to get variable names for blocks of selectors + # and CSS properties. + class NamePool(T, B) + INITIAL = 'a'.pred.to_s - def initalize(@name) - end + @cache : Hash(Tuple(B, T), String) = {} of Tuple(B, T) => String + @current : Hash(B, String) = {} of B => String - def compile(prefix) - "" + def of(subject : T, base : B) + @cache[{base, subject}] ||= begin + @current[base] = (@current[base]? || INITIAL).succ end end + end + + # This class is responsible to build the CSS of "style" tags by resolving + # nested media queries and selectors, handling cases of the same rules in + # different places. + class StyleBuilder + alias Selector = Hash(String, String) + + getter selectors, property_pool, name_pool, style_pool, variables def initialize - @js = JS.new + # Three name pools so there would be no clashes, + # which also good for optimizations. + @property_pool = NamePool(String, Selector).new + @style_pool = NamePool(Ast::Style, Nil).new + @name_pool = NamePool(Selector, Nil).new + + # This is the main data structure: + # + # Hash(Tuple(MediaQueries, Selectors), Selector) + # + # Basically it allows to identify a specific set of rules in a + # specific set of media queries in case their properties are + # defined in serveral places. + @selectors = {} of Tuple(Array(String), Array(String)) => Selector + + # This hash contains variables for a specific "style" tag, which will + # be compiled by the compiler itself when compiling an HTML element + # which uses the specific style tag. + @variables = {} of Ast::Node => Hash(String, Array(String | Ast::CssInterpolation)) end - def build(node : Ast::Style) - selector = Selector.new(js.style_of(node)) + # Compiles the processed data into a CSS style sheet. + def compile + output = {} of Array(String) => Array(String) - node.body.each do |item| - case item - when Ast::CssDefinition - selector.definitions[item.name] = item.value - when Ast::CssSelector - selector.selectors << build(item) - when Ast::Media - selector.medias << build(item) + selectors + .reject { |_, v| v.empty? } + .each do |(medias, rules), properties| + selector = + rules.join(",\n") + + body = + properties + .map { |key, value| "#{key}: #{value};" } + .join("\n") + + output[medias] ||= [] of String + output[medias] << "#{selector} {\n#{body.indent}\n}" end - end - selector + output.map do |medias, rules| + if medias.any? + "@media #{medias.join(" and ")} {\n#{rules.join("\n\n").indent}\n}" + else + rules.join("\n\n") + end + end.join("\n\n") end - def build(node : Ast::CssMedia) - selector = Selector.new(content) + # The main entry point for processing a "style" tag. + def process(node : Ast::Style) + selectors = + ["." + style_pool.of(node, nil)] - node.body.each do |item| - case item - when Ast::CssDefinition - selector.definitions[item.name] = item.value - when Ast::CssSelector - selector.selectors << build(item) + process(node.body, selectors, [] of String, node) + end + + # Processes an Ast::CssSelector + def process(node : Ast::CssSelector, + parents : Array(String), + media : Array(String), + style_node : Ast::Node) + selectors = [] of String + + parents.each do |parent| + node.selectors.map do |item| + selectors << parent + " " + item end end - selector + process(node.body, selectors, media, style_node) end - def build(node : Ast::CssSelector, prefix : String) - selector = Selector.new(node.selectors.join(", ")) + # Processes an Ast::Media + def process(node : Ast::CssMedia, + selectors : Array(String), + media : Array(String), + style_node : Ast::Node) + process(node.body, selectors, media + [node.content], style_node) + end + + # Processes the body of a CSS Ast::Node. + def process(body : Array(Ast::Node), + selectors : Array(String), + media : Array(String), + style_node : Ast::Node) + # Create a selector for this specific state + selector = + @selectors[{media, selectors}] ||= Selector.new - node.body.each do |item| + body.each do |item| case item when Ast::CssDefinition - selector.definitions[item.name] = item.value + if item.value.any?(Ast::CssInterpolation) + # Get the unique ID of the selector + block_id = + name_pool.of(selector, nil) + + # Get the unique ID of the property + variable_id = + property_pool.of(item.name, selector) + + # Get the name of the variable + variable = + "--#{block_id}-#{variable_id}" + + selector[item.name] = variable + + # Save the actual data for the variable for compiling later. + variables[style_node] ||= {} of String => Array(String | Ast::CssInterpolation) + variables[style_node][variable] = item.value + else + selector[item.name] = item.value.join("") + end when Ast::CssSelector - selector.selectors << build(item) + process(item, selectors, media, style_node) + when Ast::CssMedia + process(item, selectors, media, style_node) end end - - selector end end end From ea0bf8ab3e55df752cfa5ba3194750ca1d28828e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guszt=C3=A1v=20Szikszai?= Date: Thu, 17 Oct 2019 13:37:59 +0200 Subject: [PATCH 04/21] Use StyleBuilder in the compiler as well. --- spec/compilers/css_media | 49 +++++++++++++++++++++++++++ spec/compilers/css_selector | 7 ++-- spec/compilers/html_with_pseudos | 8 ++--- src/compiler.cr | 7 ++-- src/compilers/css_body.cr | 53 ----------------------------- src/compilers/html_element.cr | 57 +++++++++++++++++++------------- src/compilers/style.cr | 48 +-------------------------- src/compilers/top_level.cr | 25 ++------------ src/style_builder.cr | 6 ++-- src/type_checkers/artifacts.cr | 7 ++-- 10 files changed, 101 insertions(+), 166 deletions(-) create mode 100644 spec/compilers/css_media delete mode 100644 src/compilers/css_body.cr diff --git a/spec/compilers/css_media b/spec/compilers/css_media new file mode 100644 index 000000000..6719698e9 --- /dev/null +++ b/spec/compilers/css_media @@ -0,0 +1,49 @@ +component Main { + style test { + div { + color: {color}; + } + + @media (screen) { + color: {color}; + } + } + + get color : String { + "blue" + } + + fun render : Html { + + } +} +-------------------------------------------------------------------------------- +class A extends _C { + get a() { + return `blue` + } + + render() { + return _h("div", { + className: `a`, + style: { + [`--a-a`]: this.a, + [`--b-a`]: this.a + } + }) + } +} + +A.displayName = "Main" + +_insertStyles(` +.a div { + color: var(--a-a); +} + +@media (screen) { + .a { + color: var(--b-a); + } +} +`) diff --git a/spec/compilers/css_selector b/spec/compilers/css_selector index 89cf40485..df4559a86 100644 --- a/spec/compilers/css_selector +++ b/spec/compilers/css_selector @@ -1,8 +1,9 @@ component Main { style test { - & div { + div { color: {color}; } + &:focus { color: red; } @@ -35,10 +36,6 @@ class A extends _C { A.displayName = "Main" _insertStyles(` -.a { - -} - .a div { color: var(--a-a); } diff --git a/spec/compilers/html_with_pseudos b/spec/compilers/html_with_pseudos index 12d4af0ff..97810ff16 100644 --- a/spec/compilers/html_with_pseudos +++ b/spec/compilers/html_with_pseudos @@ -44,8 +44,8 @@ class A extends _C { className: `a`, style: { [`--a-a`]: this.a, - [`--a-b`]: this.b, - [`--a-c`]: `Hello` + [`--b-a`]: this.b, + [`--c-a`]: `Hello` } }) } @@ -60,12 +60,12 @@ _insertStyles(` } .a:hover { - background: var(--a-b); + background: var(--b-a); color: cyan; } .a div { - font-family: var(--a-c); + font-family: var(--c-a); color: blue; } `) diff --git a/src/compiler.cr b/src/compiler.cr index 901dc1625..0b54af9c0 100644 --- a/src/compiler.cr +++ b/src/compiler.cr @@ -1,12 +1,13 @@ module Mint class Compiler - delegate dynamic_styles, styles, ast, types, variables, to: @artifacts - delegate html_elements, medias, lookups, checked, cache, to: @artifacts + delegate html_elements, lookups, checked, cache, to: @artifacts + delegate ast, types, variables, to: @artifacts delegate record_field_lookup, to: @artifacts - getter js + getter js, style_builder def initialize(@artifacts : TypeChecker::Artifacts, @optimize = false) + @style_builder = StyleBuilder.new @js = Js.new(optimize: @optimize) @decoder = Decoder.new(@js) end diff --git a/src/compilers/css_body.cr b/src/compilers/css_body.cr deleted file mode 100644 index ac166b5b4..000000000 --- a/src/compilers/css_body.cr +++ /dev/null @@ -1,53 +0,0 @@ -module Mint - class Compiler - def compile(selector : String, key : String, - definitions : Array(Ast::CssDefinition), - media = nil) : Hash(String, String) - dynamics = {} of String => String - regulars = {} of String => String - - definitions.each do |item| - if item.value.any?(&.is_a?(Ast::CssInterpolation)) - name = - js.style_next_property key - - variable = - "--#{key}-#{name}" - - value = item.value.map do |part| - case part - when String - "`#{part}`" - else - compile part - end - end.reject(&.empty?) - .join(" + ") - - dynamics[variable] = value - regulars[item.name] = "var(#{variable})" - else - value = - item - .value - .select(&.is_a?(String)) - .join(" ") - - regulars[item.name] = value - end - end - - dynamic_styles[key] ||= {} of String => String - dynamic_styles[key].merge!(dynamics) - - if media - medias[media] ||= {} of String => Hash(String, String) - medias[media][selector] ||= {} of String => String - medias[media][selector].merge!(regulars) - else - styles[selector] ||= {} of String => String - styles[selector].merge!(regulars) - end - end - end -end diff --git a/src/compilers/html_element.cr b/src/compilers/html_element.cr index ee2480a75..e40e60c75 100644 --- a/src/compilers/html_element.cr +++ b/src/compilers/html_element.cr @@ -25,20 +25,12 @@ module Mint component = html_elements[node]? - class_name = - if node.style && component - js.style_of(lookups[node]) - end + style_node = + node.style && component && lookups[node] - class_names = - if class_name - medias - .values - .map(&.keys) - .flatten - .select(&.starts_with?(class_name + "_")) - .push(class_name) - .join(" ") + class_name = + if style_node + style_builder.style_pool.of(style_node, nil) end class_name_attribute = @@ -52,23 +44,42 @@ module Mint end classes = - if class_names && class_name_attribute_value - "#{class_name_attribute_value} + ` #{class_names}`" + if class_name && class_name_attribute_value + "#{class_name_attribute_value} + ` #{class_name}`" elsif class_name_attribute_value "#{class_name_attribute_value}" - elsif class_names - "`#{class_names}`" + elsif class_name + "`#{class_name}`" end attributes["className"] = classes if classes variables = - if styles = dynamic_styles[class_name]? - items = - styles - .each_with_object({} of String => String) { |(key, value), memo| memo["[`#{key}`]"] = value } - - js.object(items) unless items.empty? + if style_node + style_builder + .variables[style_node]? + .try do |hash| + items = hash.each_with_object({} of String => String) do |(key, value), memo| + memo["[`#{key}`]"] = + if value.any?(&.is_a?(Ast::CssInterpolation)) + value.map do |part| + case part + when String + "`#{part}`" + else + compile part + end + end.reject(&.empty?) + .join(" + ") + else + value + .select(&.is_a?(String)) + .join(" ") + end + end + + js.object(items) unless items.empty? + end end custom_styles = node diff --git a/src/compilers/style.cr b/src/compilers/style.cr index 0aa846190..34abf374a 100644 --- a/src/compilers/style.cr +++ b/src/compilers/style.cr @@ -15,53 +15,7 @@ module Mint end def _compile(node : Ast::Style, component : Ast::Component) : Nil - prefix = - js.style_of(node) - - definitions = [] of Ast::CssDefinition - selectors = [] of Ast::CssSelector - medias = [] of Ast::CssMedia - - node.body.each do |item| - case item - when Ast::CssDefinition - definitions << item - when Ast::CssSelector - selectors << item - when Ast::CssMedia - medias << item - end - end - - compile prefix, prefix, definitions - - medias.each_with_index do |item, index| - defs = [] of Ast::CssDefinition - - item.body.each do |part| - case part - when Ast::CssDefinition - defs << part - end - end - - compile prefix + "_media_#{index}", prefix, defs, item.content - end - - selectors.each do |item| - item.selectors.each do |selector| - defs = [] of Ast::CssDefinition - - item.body.each do |part| - case part - when Ast::CssDefinition - defs << part - end - end - - compile prefix + selector, prefix, defs - end - end + style_builder.process node end end end diff --git a/src/compilers/top_level.cr b/src/compilers/top_level.cr index 2dfb6b4f9..2b397f35c 100644 --- a/src/compilers/top_level.cr +++ b/src/compilers/top_level.cr @@ -69,35 +69,14 @@ module Mint enums = compile ast.enums - media_css = - medias.map do |condition, rules| - selectors = - rules.map do |name, items| - definitions = - items.map { |key, value| "#{key}: #{value};" } - - js.css_rule(".#{name}", definitions) - end - - js.css_rule("@media #{condition}", selectors) - end - - css = - styles.map do |name, items| - definitions = - items.map { |key, value| "#{key}: #{value};" } - - js.css_rule(".#{name}", definitions) - end - all_css = - css + media_css + style_builder.compile footer = if all_css.empty? "" else - "_insertStyles(`\n#{js.css_rules(all_css)}\n`)" + "_insertStyles(`\n#{all_css}\n`)" end elements = diff --git a/src/style_builder.cr b/src/style_builder.cr index dfd03c3e3..80dd7dbed 100644 --- a/src/style_builder.cr +++ b/src/style_builder.cr @@ -29,7 +29,7 @@ module Mint # Three name pools so there would be no clashes, # which also good for optimizations. @property_pool = NamePool(String, Selector).new - @style_pool = NamePool(Ast::Style, Nil).new + @style_pool = NamePool(Ast::Node, Nil).new @name_pool = NamePool(Selector, Nil).new # This is the main data structure: @@ -92,7 +92,7 @@ module Mint parents.each do |parent| node.selectors.map do |item| - selectors << parent + " " + item + selectors << parent + item end end @@ -132,7 +132,7 @@ module Mint variable = "--#{block_id}-#{variable_id}" - selector[item.name] = variable + selector[item.name] = "var(#{variable})" # Save the actual data for the variable for compiling later. variables[style_node] ||= {} of String => Array(String | Ast::CssInterpolation) diff --git a/src/type_checkers/artifacts.cr b/src/type_checkers/artifacts.cr index 000337063..86670b3a8 100644 --- a/src/type_checkers/artifacts.cr +++ b/src/type_checkers/artifacts.cr @@ -1,15 +1,12 @@ module Mint class TypeChecker class Artifacts - getter types, variables, html_elements, dynamic_styles, styles - getter ast, medias, lookups, cache, checked, record_field_lookup + getter types, variables, html_elements + getter ast, lookups, cache, checked, record_field_lookup def initialize(@ast : Ast, @html_elements = {} of Ast::HtmlElement => Ast::Component | Nil, - @medias = {} of String => Hash(String, Hash(String, String)), - @dynamic_styles = {} of String => Hash(String, String), @record_field_lookup = {} of Ast::Node => String, - @styles = {} of String => Hash(String, String), @variables = {} of Ast::Node => Scope::Lookup, @lookups = {} of Ast::Node => Ast::Node, @types = {} of Ast::Node => Checkable, From f8ced073fcb57d668922e922af7b558c51c0b449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guszt=C3=A1v=20Szikszai?= Date: Tue, 22 Oct 2019 13:43:39 +0200 Subject: [PATCH 05/21] Naive implementation of if expression for CSS. --- Makefile | 2 +- spec/compilers/css_definition | 4 +- spec/compilers/css_media | 4 +- spec/compilers/css_selector | 4 +- spec/compilers/css_with_if | 46 +++++++++++++++ spec/compilers/html_with_pseudos | 4 +- spec/compilers/html_with_style | 4 +- src/ast/if.cr | 11 ++-- src/compilers/component.cr | 7 ++- src/compilers/html_element.cr | 49 +++++++++------- src/compilers/if.cr | 7 ++- src/formatters/if.cr | 25 ++++++-- src/parsers/if.cr | 25 ++++++-- src/parsers/style.cr | 2 +- src/style_builder.cr | 99 ++++++++++++++++++++++++++++---- src/type_checkers/if.cr | 35 +++++++---- 16 files changed, 256 insertions(+), 72 deletions(-) create mode 100644 spec/compilers/css_with_if diff --git a/Makefile b/Makefile index 081557bbe..bf1f26f0d 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ build: crystal build src/mint.cr -o mint -p && mv mint ~/.bin/mint && mint test: - crystal spec -p && bin/ameba + crystal spec -p --error-trace && bin/ameba test-core: crystal build src/mint.cr -o mint -p && cd core && ../mint test -b firefox && cd .. && rm mint diff --git a/spec/compilers/css_definition b/spec/compilers/css_definition index 43312f6cb..98b6e943e 100644 --- a/spec/compilers/css_definition +++ b/spec/compilers/css_definition @@ -20,9 +20,9 @@ class A extends _C { render() { return _h("div", { className: `a`, - style: { + style: _style([{ [`--a-a`]: this.a + `px 0px` - } + }]) }) } } diff --git a/spec/compilers/css_media b/spec/compilers/css_media index 6719698e9..b117c06b9 100644 --- a/spec/compilers/css_media +++ b/spec/compilers/css_media @@ -26,10 +26,10 @@ class A extends _C { render() { return _h("div", { className: `a`, - style: { + style: _style([{ [`--a-a`]: this.a, [`--b-a`]: this.a - } + }]) }) } } diff --git a/spec/compilers/css_selector b/spec/compilers/css_selector index df4559a86..6d5b6668f 100644 --- a/spec/compilers/css_selector +++ b/spec/compilers/css_selector @@ -26,9 +26,9 @@ class A extends _C { render() { return _h("div", { className: `a`, - style: { + style: _style([{ [`--a-a`]: this.a - } + }]) }) } } diff --git a/spec/compilers/css_with_if b/spec/compilers/css_with_if new file mode 100644 index 000000000..badfbb649 --- /dev/null +++ b/spec/compilers/css_with_if @@ -0,0 +1,46 @@ +component Main { + style test { + if (true) { + color: red; + } else { + color: blue; + } + } + + fun render : Html { + + } +} +-------------------------------------------------------------------------------- +class A extends _C { + get _a() { + const _ = {} + + if (true) { + Object.assign(_, { + [`--a-a`]: `red` + }) + } else { + Object.assign(_, { + [`--a-a`]: `blue` + }) + } + + return _ + } + + render() { + return _h("div", { + className: `a`, + style: _style([this._a]) + }) + } +} + +A.displayName = "Main" + +_insertStyles(` +.a { + color: var(--a-a); +} +`) diff --git a/spec/compilers/html_with_pseudos b/spec/compilers/html_with_pseudos index 97810ff16..9852704ce 100644 --- a/spec/compilers/html_with_pseudos +++ b/spec/compilers/html_with_pseudos @@ -42,11 +42,11 @@ class A extends _C { render() { return _h("div", { className: `a`, - style: { + style: _style([{ [`--a-a`]: this.a, [`--b-a`]: this.b, [`--c-a`]: `Hello` - } + }]) }) } } diff --git a/spec/compilers/html_with_style b/spec/compilers/html_with_style index d0b0a5ffb..cf422126c 100644 --- a/spec/compilers/html_with_style +++ b/spec/compilers/html_with_style @@ -27,9 +27,9 @@ class A extends _C { render() { return _h("div", { className: `a`, - style: { + style: _style([{ [`--a-a`]: this.a - } + }]) }) } } diff --git a/src/ast/if.cr b/src/ast/if.cr index 41aaaaa83..f69c3f775 100644 --- a/src/ast/if.cr +++ b/src/ast/if.cr @@ -1,17 +1,20 @@ module Mint class Ast class If < Node - getter condition, truthy, falsy getter truthy_head_comments, truthy_tail_comments getter falsy_head_comments, falsy_tail_comments + getter condition, branches + + alias Branches = Tuple(Array(Node), Array(Node)) | + Tuple(Array(Node), If) | + Tuple(Node, Node) def initialize(@truthy_head_comments : Array(Comment), @truthy_tail_comments : Array(Comment), @falsy_head_comments : Array(Comment), @falsy_tail_comments : Array(Comment), - @condition : Expression, - @truthy : Expression, - @falsy : Expression, + @branches : Branches, + @condition : Node, @input : Data, @from : Int32, @to : Int32) diff --git a/src/compilers/component.cr b/src/compilers/component.cr index 31949e367..e6aa7cd34 100644 --- a/src/compilers/component.cr +++ b/src/compilers/component.cr @@ -6,6 +6,11 @@ module Mint compile node.styles, node + styles = + node.styles.map do |style_node| + style_builder.compile_style(style_node, self) + end.reject(&.empty?) + functions = compile_component_functions node @@ -58,7 +63,7 @@ module Mint end body = - ([constructor] + gets + states + store_stuff + functions) + ([constructor] + styles + gets + states + store_stuff + functions) .compact js.statements([ diff --git a/src/compilers/html_element.cr b/src/compilers/html_element.cr index e40e60c75..7f79f1404 100644 --- a/src/compilers/html_element.cr +++ b/src/compilers/html_element.cr @@ -1,5 +1,23 @@ module Mint class Compiler + def compile(value : Array(Ast::CssInterpolation | String)) + if value.any?(&.is_a?(Ast::CssInterpolation)) + value.map do |part| + case part + when String + "`#{part}`" + else + compile part + end + end.reject(&.empty?) + .join(" + ") + else + value + .select(&.is_a?(String)) + .join(" ") + end + end + def _compile(node : Ast::HtmlElement) : String tag = node.tag.value @@ -60,22 +78,7 @@ module Mint .variables[style_node]? .try do |hash| items = hash.each_with_object({} of String => String) do |(key, value), memo| - memo["[`#{key}`]"] = - if value.any?(&.is_a?(Ast::CssInterpolation)) - value.map do |part| - case part - when String - "`#{part}`" - else - compile part - end - end.reject(&.empty?) - .join(" + ") - else - value - .select(&.is_a?(String)) - .join(" ") - end + memo["[`#{key}`]"] = compile value end js.object(items) unless items.empty? @@ -87,12 +90,14 @@ module Mint .find(&.name.value.==("style")) .try { |attribute| compile(attribute.value) } - if custom_styles && variables - attributes["style"] = "_style([#{variables}, #{custom_styles}])" - elsif custom_styles - attributes["style"] = "_style([#{custom_styles}])" - elsif variables - attributes["style"] = variables + styles = [] of String + + styles << "this._#{class_name}" if style_builder.ifs.any?(&.first.first.==(style_node)) + styles << variables if variables + styles << custom_styles if custom_styles + + if styles.any? + attributes["style"] = "_style([#{styles.join(", ")}])" end node.ref.try do |ref| diff --git a/src/compilers/if.cr b/src/compilers/if.cr index 1d3938c5f..d655e4681 100644 --- a/src/compilers/if.cr +++ b/src/compilers/if.cr @@ -4,11 +4,14 @@ module Mint condition = compile node.condition + truthy_item, falsy_item = + node.branches + truthy = - compile node.truthy + compile truthy_item falsy = - compile node.falsy + compile falsy_item "(#{condition} ? #{truthy} : #{falsy})" end diff --git a/src/formatters/if.cr b/src/formatters/if.cr index dbfb0ea47..4e6616d91 100644 --- a/src/formatters/if.cr +++ b/src/formatters/if.cr @@ -4,17 +4,34 @@ module Mint condition = format node.condition + truthy_item, falsy_item = + node.branches + truthy = - list [node.truthy] + node.truthy_head_comments + node.truthy_tail_comments + case truthy_item + when Array(Ast::Node) + list truthy_item + node.truthy_head_comments + node.truthy_tail_comments + when Ast::Node + list [truthy_item] + node.truthy_head_comments + node.truthy_tail_comments + else + "" + end falsy = - if node.falsy.is_a?(Ast::If) && + if falsy_item.is_a?(Ast::If) && node.falsy_head_comments.empty? && node.falsy_tail_comments.empty? - format node.falsy + format falsy_item else body = - list [node.falsy] + node.falsy_head_comments + node.falsy_tail_comments + case falsy_item + when Array(Ast::Node) + list falsy_item + node.falsy_head_comments + node.falsy_tail_comments + when Ast::Node + list [falsy_item] + node.falsy_head_comments + node.falsy_tail_comments + else + "" + end "{\n#{indent(body)}\n}" end diff --git a/src/parsers/if.cr b/src/parsers/if.cr index 023a23a8b..766439dc2 100644 --- a/src/parsers/if.cr +++ b/src/parsers/if.cr @@ -11,7 +11,15 @@ module Mint syntax_error IfExpectedCondition syntax_error IfExpectedElse - def if_expression : Ast::If | Nil + def if_expression + if_expression { } + end + + def css_if_expression + if_expression(true) { css_definition } + end + + def if_expression(multiple = false, &block : -> Ast::Node | Nil) : Ast::If | Nil start do |start_position| skip unless keyword "if" @@ -27,7 +35,11 @@ module Mint opening_bracket: IfExpectedTruthyOpeningBracket, closing_bracket: IfExpectedTruthyClosingBracket ) do - expression! IfExpectedTruthyExpression + if multiple + many { block.call }.compact + else + expression! IfExpectedTruthyExpression + end end whitespace @@ -43,7 +55,11 @@ module Mint opening_bracket: IfExpectedFalsyOpeningBracket, closing_bracket: IfExpectedFalsyClosingBracket ) do - expression! IfExpectedFalsyExpression + if multiple + many { block.call }.compact + else + expression! IfExpectedFalsyExpression + end end end @@ -53,8 +69,7 @@ module Mint falsy_head_comments: falsy_head_comments, falsy_tail_comments: falsy_tail_comments, condition: condition.as(Ast::Expression), - truthy: truthy.as(Ast::Expression), - falsy: falsy.as(Ast::Expression), + branches: {truthy, falsy}, from: start_position, to: position, input: data) diff --git a/src/parsers/style.cr b/src/parsers/style.cr index 8167dfef2..51c586a0e 100644 --- a/src/parsers/style.cr +++ b/src/parsers/style.cr @@ -29,7 +29,7 @@ module Mint end def css_body - many { comment || css_definition || css_media || css_selector }.compact + many { comment || css_definition || css_if_expression || css_media || css_selector }.compact end end end diff --git a/src/style_builder.cr b/src/style_builder.cr index 80dd7dbed..a31a23b6e 100644 --- a/src/style_builder.cr +++ b/src/style_builder.cr @@ -23,7 +23,7 @@ module Mint class StyleBuilder alias Selector = Hash(String, String) - getter selectors, property_pool, name_pool, style_pool, variables + getter selectors, property_pool, name_pool, style_pool, variables, ifs def initialize # Three name pools so there would be no clashes, @@ -45,6 +45,7 @@ module Mint # be compiled by the compiler itself when compiling an HTML element # which uses the specific style tag. @variables = {} of Ast::Node => Hash(String, Array(String | Ast::CssInterpolation)) + @ifs = {} of Tuple(Ast::Node, Selector) => Array(Ast::If) end # Compiles the processed data into a CSS style sheet. @@ -75,6 +76,77 @@ module Mint end.join("\n\n") end + def compile_style(node : Ast::Style, compiler : Compiler) + ifs.select(&.first.==(node)) + .map do |(_, selector), selector_ifs| + selector_ifs.map do |item| + condition = + compiler.compile item.condition + + thruty, falsy = + item.branches + + thruty_items = + case thruty + when Array(Ast::Node) + items = + thruty + .select(&.is_a?(Ast::CssDefinition)) + .each_with_object({} of String => String) do |definition, memo| + a = + definition.as(Ast::CssDefinition) + + variable = + variable_name a.name, selector + + selector[a.name] = + "var(#{variable})" + + value = + compiler.compile a.value + + memo["[`#{variable}`]"] = "`#{value}`" + end + + compiler.js.object(items) + end + + falsy_items = + case falsy + when Array(Ast::Node) + items = + falsy + .select(&.is_a?(Ast::CssDefinition)) + .each_with_object({} of String => String) do |definition, memo| + a = + definition.as(Ast::CssDefinition) + + variable = + variable_name a.name, selector + + selector[a.name] = + "var(#{variable})" + + value = + compiler.compile a.value + + memo["[`#{variable}`]"] = "`#{value}`" + end + + compiler.js.object(items) + end + + compiler.js.get("_" + style_pool.of(node, nil), + compiler.js.statements([ + compiler.js.const("_", "{}"), + compiler.js.if(condition, "Object.assign(_, #{thruty_items})") + " " + + compiler.js.else { "Object.assign(_, #{falsy_items})" }, + compiler.js.return("_"), + ])) + end.join("\n\n") + end.join("\n\n") + end + # The main entry point for processing a "style" tag. def process(node : Ast::Style) selectors = @@ -120,17 +192,9 @@ module Mint case item when Ast::CssDefinition if item.value.any?(Ast::CssInterpolation) - # Get the unique ID of the selector - block_id = - name_pool.of(selector, nil) - - # Get the unique ID of the property - variable_id = - property_pool.of(item.name, selector) - # Get the name of the variable variable = - "--#{block_id}-#{variable_id}" + variable_name(item.name, selector) selector[item.name] = "var(#{variable})" @@ -144,8 +208,23 @@ module Mint process(item, selectors, media, style_node) when Ast::CssMedia process(item, selectors, media, style_node) + when Ast::If + ifs[{style_node, selector}] ||= [] of Ast::If + ifs[{style_node, selector}] << item end end end + + private def variable_name(name, selector) + # Get the unique ID of the selector + block_id = + name_pool.of(selector, nil) + + # Get the unique ID of the property + variable_id = + property_pool.of(name, selector) + + "--#{block_id}-#{variable_id}" + end end end diff --git a/src/type_checkers/if.cr b/src/type_checkers/if.cr index aafd5dfca..42ec90f3d 100644 --- a/src/type_checkers/if.cr +++ b/src/type_checkers/if.cr @@ -7,25 +7,36 @@ module Mint condition = resolve node.condition - truthy = - resolve node.truthy - - falsy = - resolve node.falsy - raise IfConditionTypeMismatch, { "node" => node.condition, "got" => condition, "expected" => BOOL, } unless Comparer.compare(condition, BOOL) - raise IfElseTypeMismatch, { - "node" => node.falsy, - "expected" => truthy, - "got" => falsy, - } unless Comparer.compare(truthy, falsy) + truthy_item, falsy_item = + node.branches + + if truthy_item.is_a?(Ast::Node) && + falsy_item.is_a?(Ast::Node) + truthy = + resolve truthy_item.as(Ast::Node) + + falsy = + resolve falsy_item.as(Ast::Node) + + raise IfElseTypeMismatch, { + "node" => falsy_item.as(Ast::Node), + "expected" => truthy, + "got" => falsy, + } unless Comparer.compare(truthy, falsy) + + truthy + else + resolve truthy_item + resolve falsy_item - truthy + NEVER + end end end end From 93de982aac493dadb2c7951b0f4e26e920ec163d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guszt=C3=A1v=20Szikszai?= Date: Wed, 23 Oct 2019 10:40:43 +0200 Subject: [PATCH 06/21] Make else branch optional for CSS if. --- spec/style_builder_spec.cr | 4 ++++ src/ast/if.cr | 1 + src/compilers/if.cr | 2 +- src/parsers/if.cr | 40 +++++++++++++++++++++----------------- src/type_checkers/if.cr | 2 +- 5 files changed, 29 insertions(+), 20 deletions(-) diff --git a/spec/style_builder_spec.cr b/spec/style_builder_spec.cr index 07e08410c..afce7a825 100644 --- a/spec/style_builder_spec.cr +++ b/spec/style_builder_spec.cr @@ -32,6 +32,10 @@ describe Mint::StyleBuilder do @media (screen) { div, p { font-size: 30px; + + if (true) { + color: red; + } } } diff --git a/src/ast/if.cr b/src/ast/if.cr index f69c3f775..bbd750d1b 100644 --- a/src/ast/if.cr +++ b/src/ast/if.cr @@ -6,6 +6,7 @@ module Mint getter condition, branches alias Branches = Tuple(Array(Node), Array(Node)) | + Tuple(Array(Node), Nil) | Tuple(Array(Node), If) | Tuple(Node, Node) diff --git a/src/compilers/if.cr b/src/compilers/if.cr index d655e4681..408f3607f 100644 --- a/src/compilers/if.cr +++ b/src/compilers/if.cr @@ -11,7 +11,7 @@ module Mint compile truthy_item falsy = - compile falsy_item + falsy_item.try { |item| compile item } "(#{condition} ? #{truthy} : #{falsy})" end diff --git a/src/parsers/if.cr b/src/parsers/if.cr index 766439dc2..e3f61bcd4 100644 --- a/src/parsers/if.cr +++ b/src/parsers/if.cr @@ -19,7 +19,7 @@ module Mint if_expression(true) { css_definition } end - def if_expression(multiple = false, &block : -> Ast::Node | Nil) : Ast::If | Nil + def if_expression(for_css = false, &block : -> Ast::Node | Nil) : Ast::If | Nil start do |start_position| skip unless keyword "if" @@ -35,32 +35,36 @@ module Mint opening_bracket: IfExpectedTruthyOpeningBracket, closing_bracket: IfExpectedTruthyClosingBracket ) do - if multiple + if for_css many { block.call }.compact else expression! IfExpectedTruthyExpression end end + falsy_head_comments = [] of Ast::Comment + falsy_tail_comments = [] of Ast::Comment + falsy = nil + whitespace - keyword! "else", IfExpectedElse - whitespace - if falsy = if_expression - falsy_head_comments = [] of Ast::Comment - falsy_tail_comments = [] of Ast::Comment - else - falsy_head_comments, falsy, falsy_tail_comments = - block_with_comments( - opening_bracket: IfExpectedFalsyOpeningBracket, - closing_bracket: IfExpectedFalsyClosingBracket - ) do - if multiple - many { block.call }.compact - else - expression! IfExpectedFalsyExpression + if !for_css || keyword_ahead "else" + keyword! "else", IfExpectedElse + whitespace + + unless falsy = if_expression + falsy_head_comments, falsy, falsy_tail_comments = + block_with_comments( + opening_bracket: IfExpectedFalsyOpeningBracket, + closing_bracket: IfExpectedFalsyClosingBracket + ) do + if for_css + many { block.call }.compact + else + expression! IfExpectedFalsyExpression + end end - end + end end Ast::If.new( diff --git a/src/type_checkers/if.cr b/src/type_checkers/if.cr index 42ec90f3d..964c9f399 100644 --- a/src/type_checkers/if.cr +++ b/src/type_checkers/if.cr @@ -33,7 +33,7 @@ module Mint truthy else resolve truthy_item - resolve falsy_item + falsy_item.try { |data| resolve data } NEVER end From 00ad488a4202d4d1f85c2dbef64ed816425edb99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guszt=C3=A1v=20Szikszai?= Date: Wed, 23 Oct 2019 10:58:12 +0200 Subject: [PATCH 07/21] Move static variables compiling to style builder. --- spec/compilers/css_definition | 12 +- spec/compilers/css_media | 14 +- spec/compilers/css_selector | 12 +- spec/compilers/html_with_pseudos | 16 +- spec/compilers/html_with_style | 12 +- .../html_with_style_and_custom_style | 12 +- src/compilers/html_element.cr | 16 +- src/style_builder.cr | 143 +++++++++--------- 8 files changed, 133 insertions(+), 104 deletions(-) diff --git a/spec/compilers/css_definition b/spec/compilers/css_definition index 98b6e943e..a0bd062eb 100644 --- a/spec/compilers/css_definition +++ b/spec/compilers/css_definition @@ -13,6 +13,14 @@ component Main { } -------------------------------------------------------------------------------- class A extends _C { + get _a() { + const _ = { + [`--a-a`]: this.a + `px 0px` + } + + return _ + } + get a() { return 10 } @@ -20,9 +28,7 @@ class A extends _C { render() { return _h("div", { className: `a`, - style: _style([{ - [`--a-a`]: this.a + `px 0px` - }]) + style: _style([this._a]) }) } } diff --git a/spec/compilers/css_media b/spec/compilers/css_media index b117c06b9..51196fa98 100644 --- a/spec/compilers/css_media +++ b/spec/compilers/css_media @@ -19,6 +19,15 @@ component Main { } -------------------------------------------------------------------------------- class A extends _C { + get _a() { + const _ = { + [`--a-a`]: this.a, + [`--b-a`]: this.a + } + + return _ + } + get a() { return `blue` } @@ -26,10 +35,7 @@ class A extends _C { render() { return _h("div", { className: `a`, - style: _style([{ - [`--a-a`]: this.a, - [`--b-a`]: this.a - }]) + style: _style([this._a]) }) } } diff --git a/spec/compilers/css_selector b/spec/compilers/css_selector index 6d5b6668f..38bc9feaa 100644 --- a/spec/compilers/css_selector +++ b/spec/compilers/css_selector @@ -19,6 +19,14 @@ component Main { } -------------------------------------------------------------------------------- class A extends _C { + get _a() { + const _ = { + [`--a-a`]: this.a + } + + return _ + } + get a() { return `blue` } @@ -26,9 +34,7 @@ class A extends _C { render() { return _h("div", { className: `a`, - style: _style([{ - [`--a-a`]: this.a - }]) + style: _style([this._a]) }) } } diff --git a/spec/compilers/html_with_pseudos b/spec/compilers/html_with_pseudos index 9852704ce..a6f01c7f0 100644 --- a/spec/compilers/html_with_pseudos +++ b/spec/compilers/html_with_pseudos @@ -39,14 +39,20 @@ class A extends _C { }) } + get _a() { + const _ = { + [`--a-a`]: this.a, + [`--b-a`]: this.b, + [`--c-a`]: `Hello` + } + + return _ + } + render() { return _h("div", { className: `a`, - style: _style([{ - [`--a-a`]: this.a, - [`--b-a`]: this.b, - [`--c-a`]: `Hello` - }]) + style: _style([this._a]) }) } } diff --git a/spec/compilers/html_with_style b/spec/compilers/html_with_style index cf422126c..b63b93a73 100644 --- a/spec/compilers/html_with_style +++ b/spec/compilers/html_with_style @@ -24,12 +24,18 @@ class A extends _C { }) } + get _a() { + const _ = { + [`--a-a`]: this.a + } + + return _ + } + render() { return _h("div", { className: `a`, - style: _style([{ - [`--a-a`]: this.a - }]) + style: _style([this._a]) }) } } diff --git a/spec/compilers/html_with_style_and_custom_style b/spec/compilers/html_with_style_and_custom_style index e86289ab5..5091fa6df 100644 --- a/spec/compilers/html_with_style_and_custom_style +++ b/spec/compilers/html_with_style_and_custom_style @@ -28,6 +28,14 @@ class A extends _C { }) } + get _a() { + const _ = { + [`--a-a`]: this.a + } + + return _ + } + get b() { return } @@ -35,9 +43,7 @@ class A extends _C { render() { return _h("div", { className: `a`, - style: _style([{ - [`--a-a`]: this.a - }, this.b]) + style: _style([this._a, this.b]) }) } } diff --git a/src/compilers/html_element.cr b/src/compilers/html_element.cr index 7f79f1404..23dc7b4e1 100644 --- a/src/compilers/html_element.cr +++ b/src/compilers/html_element.cr @@ -72,19 +72,6 @@ module Mint attributes["className"] = classes if classes - variables = - if style_node - style_builder - .variables[style_node]? - .try do |hash| - items = hash.each_with_object({} of String => String) do |(key, value), memo| - memo["[`#{key}`]"] = compile value - end - - js.object(items) unless items.empty? - end - end - custom_styles = node .attributes .find(&.name.value.==("style")) @@ -92,8 +79,7 @@ module Mint styles = [] of String - styles << "this._#{class_name}" if style_builder.ifs.any?(&.first.first.==(style_node)) - styles << variables if variables + styles << "this._#{class_name}" if style_builder.any?(style_node) styles << custom_styles if custom_styles if styles.any? diff --git a/src/style_builder.cr b/src/style_builder.cr index a31a23b6e..d2f8e934d 100644 --- a/src/style_builder.cr +++ b/src/style_builder.cr @@ -76,75 +76,82 @@ module Mint end.join("\n\n") end + private def compile_branch(items : Array(Ast::Node), compiler : Compiler, selector : Selector) + compiled = items + .select(&.is_a?(Ast::CssDefinition)) + .each_with_object({} of String => String) do |definition, memo| + a = + definition.as(Ast::CssDefinition) + + variable = + variable_name a.name, selector + + selector[a.name] = + "var(#{variable})" + + value = + compiler.compile a.value + + memo["[`#{variable}`]"] = "`#{value}`" + end + + compiler.js.object(compiled) + end + + def any?(node : Ast::Node) + variables[node]? || ifs.any?(&.first.first.==(node)) + end + + def any?(node : Nil) + false + end + def compile_style(node : Ast::Style, compiler : Compiler) - ifs.select(&.first.==(node)) - .map do |(_, selector), selector_ifs| - selector_ifs.map do |item| - condition = - compiler.compile item.condition - - thruty, falsy = - item.branches - - thruty_items = - case thruty - when Array(Ast::Node) - items = - thruty - .select(&.is_a?(Ast::CssDefinition)) - .each_with_object({} of String => String) do |definition, memo| - a = - definition.as(Ast::CssDefinition) - - variable = - variable_name a.name, selector - - selector[a.name] = - "var(#{variable})" - - value = - compiler.compile a.value - - memo["[`#{variable}`]"] = "`#{value}`" - end - - compiler.js.object(items) - end - - falsy_items = - case falsy - when Array(Ast::Node) - items = - falsy - .select(&.is_a?(Ast::CssDefinition)) - .each_with_object({} of String => String) do |definition, memo| - a = - definition.as(Ast::CssDefinition) - - variable = - variable_name a.name, selector - - selector[a.name] = - "var(#{variable})" - - value = - compiler.compile a.value - - memo["[`#{variable}`]"] = "`#{value}`" - end - - compiler.js.object(items) - end - - compiler.js.get("_" + style_pool.of(node, nil), - compiler.js.statements([ - compiler.js.const("_", "{}"), - compiler.js.if(condition, "Object.assign(_, #{thruty_items})") + " " + - compiler.js.else { "Object.assign(_, #{falsy_items})" }, - compiler.js.return("_"), - ])) + return "" unless any?(node) + + static = + variables[node]? + .try do |hash| + items = hash.each_with_object({} of String => String) do |(key, value), memo| + memo["[`#{key}`]"] = compiler.compile value + end + + compiler.js.object(items) unless items.empty? + end || "{}" + + compiled_ifs = + ifs.select(&.first.==(node)) + .map do |(_, selector), selector_ifs| + selector_ifs.map do |item| + condition = + compiler.compile item.condition + + thruty, falsy = + item.branches + + thruty_items = + case thruty + when Array(Ast::Node) + compile_branch thruty, compiler, selector + end + + falsy_items = + case falsy + when Array(Ast::Node) + compile_branch falsy, compiler, selector + end + + compiler.js.if(condition, "Object.assign(_, #{thruty_items})") + " " + + compiler.js.else { "Object.assign(_, #{falsy_items})" } + end.join("\n\n") end.join("\n\n") - end.join("\n\n") + + compiler.js.get("_" + style_pool.of(node, nil), + compiler.js.statements([[ + compiler.js.const("_", static), + compiled_ifs, + compiler.js.return("_"), + ]].flatten.reject(&.empty?))) end # The main entry point for processing a "style" tag. @@ -155,7 +162,7 @@ module Mint process(node.body, selectors, [] of String, node) end - # Processes an Ast::CssSelector + # Processes a Ast::CssSelector def process(node : Ast::CssSelector, parents : Array(String), media : Array(String), From b031a6490ba434ecd1cee8e96c39cb0306f333db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guszt=C3=A1v=20Szikszai?= Date: Wed, 23 Oct 2019 11:08:53 +0200 Subject: [PATCH 08/21] Refactor variable compiler of style builder into it's own class. --- src/style_builder.cr | 170 +++++++++++++++++++++++++------------------ 1 file changed, 101 insertions(+), 69 deletions(-) diff --git a/src/style_builder.cr b/src/style_builder.cr index d2f8e934d..ce9911a15 100644 --- a/src/style_builder.cr +++ b/src/style_builder.cr @@ -17,6 +17,102 @@ module Mint end end + # Compiles the variables of a style node into a computed property. + class VariableCompiler + delegate ifs, variables, variable_name, any?, style_pool, to: @builder + delegate js, compile, to: @compiler + + getter compiler : Compiler + getter builder : StyleBuilder + + def initialize(@builder, @compiler) + end + + def compile(node : Ast::Style) + return "" unless any?(node) + + static = + variables[node]? + .try do |hash| + items = + hash + .each_with_object({} of String => String) do |(key, value), memo| + memo["[`#{key}`]"] = compile value + end + + js.object(items) unless items.empty? + end || "{}" + + compiled_ifs = + ifs + .select(&.first.==(node)) + .map do |(_, selector), selector_ifs| + statements = + selector_ifs.map do |item| + condition = + compile item.condition + + thruty, falsy = + item.branches + + thruty_items = + case thruty + when Array(Ast::Node) + compile_branch thruty, selector + end + + falsy_items = + case falsy + when Array(Ast::Node) + compile_branch falsy, selector + end + + thruty_branch = + js.if(condition, "Object.assign(_, #{thruty_items})") + + falsy_branch = + if falsy_items + js.else { "Object.assign(_, #{falsy_items})" } + end + + [thruty_branch, falsy_branch] + .compact + .join(" ") + end + + js.statements(statements) + end + + js.get("_" + style_pool.of(node, nil), + js.statements([[ + js.const("_", static), + compiled_ifs, + js.return("_"), + ]].flatten.reject(&.empty?))) + end + + def compile_branch(items : Array(Ast::Node), selector : StyleBuilder::Selector) + compiled = + items + .select(&.is_a?(Ast::CssDefinition)) + .map(&.as(Ast::CssDefinition)) + .each_with_object({} of String => String) do |definition, memo| + variable = + variable_name definition.name, selector + + selector[definition.name] = + "var(#{variable})" + + value = + compile definition.value + + memo["[`#{variable}`]"] = "`#{value}`" + end + + js.object(compiled) + end + end + # This class is responsible to build the CSS of "style" tags by resolving # nested media queries and selectors, handling cases of the same rules in # different places. @@ -76,26 +172,10 @@ module Mint end.join("\n\n") end - private def compile_branch(items : Array(Ast::Node), compiler : Compiler, selector : Selector) - compiled = items - .select(&.is_a?(Ast::CssDefinition)) - .each_with_object({} of String => String) do |definition, memo| - a = - definition.as(Ast::CssDefinition) - - variable = - variable_name a.name, selector - - selector[a.name] = - "var(#{variable})" - - value = - compiler.compile a.value - - memo["[`#{variable}`]"] = "`#{value}`" - end - - compiler.js.object(compiled) + def compile_style(node : Ast::Style, compiler : Compiler) + VariableCompiler + .new(self, compiler) + .compile(node) end def any?(node : Ast::Node) @@ -106,54 +186,6 @@ module Mint false end - def compile_style(node : Ast::Style, compiler : Compiler) - return "" unless any?(node) - - static = - variables[node]? - .try do |hash| - items = hash.each_with_object({} of String => String) do |(key, value), memo| - memo["[`#{key}`]"] = compiler.compile value - end - - compiler.js.object(items) unless items.empty? - end || "{}" - - compiled_ifs = - ifs.select(&.first.==(node)) - .map do |(_, selector), selector_ifs| - selector_ifs.map do |item| - condition = - compiler.compile item.condition - - thruty, falsy = - item.branches - - thruty_items = - case thruty - when Array(Ast::Node) - compile_branch thruty, compiler, selector - end - - falsy_items = - case falsy - when Array(Ast::Node) - compile_branch falsy, compiler, selector - end - - compiler.js.if(condition, "Object.assign(_, #{thruty_items})") + " " + - compiler.js.else { "Object.assign(_, #{falsy_items})" } - end.join("\n\n") - end.join("\n\n") - - compiler.js.get("_" + style_pool.of(node, nil), - compiler.js.statements([[ - compiler.js.const("_", static), - compiled_ifs, - compiler.js.return("_"), - ]].flatten.reject(&.empty?))) - end - # The main entry point for processing a "style" tag. def process(node : Ast::Style) selectors = @@ -222,7 +254,7 @@ module Mint end end - private def variable_name(name, selector) + def variable_name(name, selector) # Get the unique ID of the selector block_id = name_pool.of(selector, nil) From 2942d82f2fa8cf305b6b0443493c343260082f99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guszt=C3=A1v=20Szikszai?= Date: Wed, 23 Oct 2019 11:58:10 +0200 Subject: [PATCH 09/21] Add fallback value to variables. --- spec/compilers/css_media_with_if | 50 ++++++++++++++++++++++++++++++++ spec/compilers/css_with_if | 4 ++- src/style_builder.cr | 29 ++++++++++++++---- 3 files changed, 76 insertions(+), 7 deletions(-) create mode 100644 spec/compilers/css_media_with_if diff --git a/spec/compilers/css_media_with_if b/spec/compilers/css_media_with_if new file mode 100644 index 000000000..3cb10c19d --- /dev/null +++ b/spec/compilers/css_media_with_if @@ -0,0 +1,50 @@ +component Main { + style test { + color: yellow; + + @media (max-width: 300px) { + if (true) { + color: red; + } + } + } + + fun render : Html { + + } +} +-------------------------------------------------------------------------------- +class A extends _C { + get _a() { + const _ = {} + + if (true) { + Object.assign(_, { + [`--a-a`]: `red` + }) + } + + return _ + } + + render() { + return _h("div", { + className: `a`, + style: _style([this._a]) + }) + } +} + +A.displayName = "Main" + +_insertStyles(` +.a { + color: yellow; +} + +@media (max-width: 300px) { + .a { + color: var(--a-a); + } +} +`) diff --git a/spec/compilers/css_with_if b/spec/compilers/css_with_if index badfbb649..eae2b75a6 100644 --- a/spec/compilers/css_with_if +++ b/spec/compilers/css_with_if @@ -1,5 +1,7 @@ component Main { style test { + color: yellow; + if (true) { color: red; } else { @@ -41,6 +43,6 @@ A.displayName = "Main" _insertStyles(` .a { - color: var(--a-a); + color: var(--a-a, yellow); } `) diff --git a/src/style_builder.cr b/src/style_builder.cr index ce9911a15..ab7f51087 100644 --- a/src/style_builder.cr +++ b/src/style_builder.cr @@ -17,6 +17,21 @@ module Mint end end + class PropertyValue + property default : String | Nil + property variable : String | Nil + + def to_s + if variable && default + "var(#{variable}, #{default})" + elsif variable + "var(#{variable})" + else + default + end + end + end + # Compiles the variables of a style node into a computed property. class VariableCompiler delegate ifs, variables, variable_name, any?, style_pool, to: @builder @@ -100,8 +115,8 @@ module Mint variable = variable_name definition.name, selector - selector[definition.name] = - "var(#{variable})" + selector[definition.name] ||= PropertyValue.new + selector[definition.name].variable = variable value = compile definition.value @@ -117,7 +132,7 @@ module Mint # nested media queries and selectors, handling cases of the same rules in # different places. class StyleBuilder - alias Selector = Hash(String, String) + alias Selector = Hash(String, PropertyValue) getter selectors, property_pool, name_pool, style_pool, variables, ifs @@ -156,7 +171,7 @@ module Mint body = properties - .map { |key, value| "#{key}: #{value};" } + .map { |key, value| "#{key}: #{value.to_s};" } .join("\n") output[medias] ||= [] of String @@ -235,13 +250,15 @@ module Mint variable = variable_name(item.name, selector) - selector[item.name] = "var(#{variable})" + selector[item.name] ||= PropertyValue.new + selector[item.name].variable = variable # Save the actual data for the variable for compiling later. variables[style_node] ||= {} of String => Array(String | Ast::CssInterpolation) variables[style_node][variable] = item.value else - selector[item.name] = item.value.join("") + selector[item.name] ||= PropertyValue.new + selector[item.name].default = item.value.join("") end when Ast::CssSelector process(item, selectors, media, style_node) From 5e521f1e2a4ff51e49f832d1d39b57da8891b44d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guszt=C3=A1v=20Szikszai?= Date: Thu, 24 Oct 2019 11:25:26 +0200 Subject: [PATCH 10/21] Use a function instead of a get for styles. --- spec/compilers/css_definition | 4 ++-- spec/compilers/css_media | 4 ++-- spec/compilers/css_media_with_if | 4 ++-- spec/compilers/css_selector | 4 ++-- spec/compilers/css_with_if | 4 ++-- spec/compilers/html_with_pseudos | 4 ++-- spec/compilers/html_with_style | 4 ++-- spec/compilers/html_with_style_and_custom_style | 4 ++-- src/compilers/html_element.cr | 2 +- src/style_builder.cr | 2 +- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/spec/compilers/css_definition b/spec/compilers/css_definition index a0bd062eb..4da680fde 100644 --- a/spec/compilers/css_definition +++ b/spec/compilers/css_definition @@ -13,7 +13,7 @@ component Main { } -------------------------------------------------------------------------------- class A extends _C { - get _a() { + _a() { const _ = { [`--a-a`]: this.a + `px 0px` } @@ -28,7 +28,7 @@ class A extends _C { render() { return _h("div", { className: `a`, - style: _style([this._a]) + style: _style([this._a()]) }) } } diff --git a/spec/compilers/css_media b/spec/compilers/css_media index 51196fa98..435f7864f 100644 --- a/spec/compilers/css_media +++ b/spec/compilers/css_media @@ -19,7 +19,7 @@ component Main { } -------------------------------------------------------------------------------- class A extends _C { - get _a() { + _a() { const _ = { [`--a-a`]: this.a, [`--b-a`]: this.a @@ -35,7 +35,7 @@ class A extends _C { render() { return _h("div", { className: `a`, - style: _style([this._a]) + style: _style([this._a()]) }) } } diff --git a/spec/compilers/css_media_with_if b/spec/compilers/css_media_with_if index 3cb10c19d..5fd28771c 100644 --- a/spec/compilers/css_media_with_if +++ b/spec/compilers/css_media_with_if @@ -15,7 +15,7 @@ component Main { } -------------------------------------------------------------------------------- class A extends _C { - get _a() { + _a() { const _ = {} if (true) { @@ -30,7 +30,7 @@ class A extends _C { render() { return _h("div", { className: `a`, - style: _style([this._a]) + style: _style([this._a()]) }) } } diff --git a/spec/compilers/css_selector b/spec/compilers/css_selector index 38bc9feaa..afb029b28 100644 --- a/spec/compilers/css_selector +++ b/spec/compilers/css_selector @@ -19,7 +19,7 @@ component Main { } -------------------------------------------------------------------------------- class A extends _C { - get _a() { + _a() { const _ = { [`--a-a`]: this.a } @@ -34,7 +34,7 @@ class A extends _C { render() { return _h("div", { className: `a`, - style: _style([this._a]) + style: _style([this._a()]) }) } } diff --git a/spec/compilers/css_with_if b/spec/compilers/css_with_if index eae2b75a6..f3bac2c42 100644 --- a/spec/compilers/css_with_if +++ b/spec/compilers/css_with_if @@ -15,7 +15,7 @@ component Main { } -------------------------------------------------------------------------------- class A extends _C { - get _a() { + _a() { const _ = {} if (true) { @@ -34,7 +34,7 @@ class A extends _C { render() { return _h("div", { className: `a`, - style: _style([this._a]) + style: _style([this._a()]) }) } } diff --git a/spec/compilers/html_with_pseudos b/spec/compilers/html_with_pseudos index a6f01c7f0..2173041ce 100644 --- a/spec/compilers/html_with_pseudos +++ b/spec/compilers/html_with_pseudos @@ -39,7 +39,7 @@ class A extends _C { }) } - get _a() { + _a() { const _ = { [`--a-a`]: this.a, [`--b-a`]: this.b, @@ -52,7 +52,7 @@ class A extends _C { render() { return _h("div", { className: `a`, - style: _style([this._a]) + style: _style([this._a()]) }) } } diff --git a/spec/compilers/html_with_style b/spec/compilers/html_with_style index b63b93a73..db4bab89f 100644 --- a/spec/compilers/html_with_style +++ b/spec/compilers/html_with_style @@ -24,7 +24,7 @@ class A extends _C { }) } - get _a() { + _a() { const _ = { [`--a-a`]: this.a } @@ -35,7 +35,7 @@ class A extends _C { render() { return _h("div", { className: `a`, - style: _style([this._a]) + style: _style([this._a()]) }) } } diff --git a/spec/compilers/html_with_style_and_custom_style b/spec/compilers/html_with_style_and_custom_style index 5091fa6df..f55d0fadb 100644 --- a/spec/compilers/html_with_style_and_custom_style +++ b/spec/compilers/html_with_style_and_custom_style @@ -28,7 +28,7 @@ class A extends _C { }) } - get _a() { + _a() { const _ = { [`--a-a`]: this.a } @@ -43,7 +43,7 @@ class A extends _C { render() { return _h("div", { className: `a`, - style: _style([this._a, this.b]) + style: _style([this._a(), this.b]) }) } } diff --git a/src/compilers/html_element.cr b/src/compilers/html_element.cr index 23dc7b4e1..1a3e1cc1e 100644 --- a/src/compilers/html_element.cr +++ b/src/compilers/html_element.cr @@ -79,7 +79,7 @@ module Mint styles = [] of String - styles << "this._#{class_name}" if style_builder.any?(style_node) + styles << "this._#{class_name}()" if style_builder.any?(style_node) styles << custom_styles if custom_styles if styles.any? diff --git a/src/style_builder.cr b/src/style_builder.cr index ab7f51087..5f807a8a7 100644 --- a/src/style_builder.cr +++ b/src/style_builder.cr @@ -98,7 +98,7 @@ module Mint js.statements(statements) end - js.get("_" + style_pool.of(node, nil), + js.function("_" + style_pool.of(node, nil), [] of String, js.statements([[ js.const("_", static), compiled_ifs, From 4a53b55dc6dd29171ec0aad91ab1e354b1d3ad18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guszt=C3=A1v=20Szikszai?= Date: Thu, 24 Oct 2019 12:14:45 +0200 Subject: [PATCH 11/21] Allow multiple styles on HTML elements. --- spec/compilers/html_with_multiple_styles | 34 ++++++++++++++++++++++++ spec/parsers/html_element_spec.cr | 2 ++ src/ast/html_element.cr | 4 +-- src/compiler.cr | 2 +- src/compilers/html_element.cr | 19 +++++++++---- src/formatters/html_element.cr | 8 +++--- src/parsers/html_element.cr | 19 +++++++++++-- src/type_checker.cr | 2 +- src/type_checkers/artifacts.cr | 3 ++- src/type_checkers/html_element.cr | 29 +++++++++++--------- 10 files changed, 94 insertions(+), 28 deletions(-) create mode 100644 spec/compilers/html_with_multiple_styles diff --git a/spec/compilers/html_with_multiple_styles b/spec/compilers/html_with_multiple_styles new file mode 100644 index 000000000..97630fef1 --- /dev/null +++ b/spec/compilers/html_with_multiple_styles @@ -0,0 +1,34 @@ +component Main { + style one { + color: red; + } + + style two { + color: blue; + } + + fun render : Html { + + + } +} +-------------------------------------------------------------------------------- +class A extends _C { + render() { + return _h("div", { + className: `a b` + }) + } +} + +A.displayName = "Main" + +_insertStyles(` +.a { + color: red; +} + +.b { + color: blue; +} +`) diff --git a/spec/parsers/html_element_spec.cr b/spec/parsers/html_element_spec.cr index 1196f6782..d8b68e4d9 100644 --- a/spec/parsers/html_element_spec.cr +++ b/spec/parsers/html_element_spec.cr @@ -14,6 +14,8 @@ describe "Html Element" do expect_error "" + expect_ok "" + expect_ok "" expect_ok "" expect_ok " " expect_ok " " diff --git a/src/ast/html_element.cr b/src/ast/html_element.cr index c0e365181..31c4e842f 100644 --- a/src/ast/html_element.cr +++ b/src/ast/html_element.cr @@ -1,12 +1,12 @@ module Mint class Ast class HtmlElement < Node - getter attributes, children, style, tag, comments, ref + getter attributes, children, styles, tag, comments, ref def initialize(@attributes : Array(HtmlAttribute), + @styles : Array(Variable) | Nil, @children : Array(HtmlContent), @comments : Array(Comment), - @style : Variable | Nil, @ref : Variable | Nil, @tag : Variable, @input : Data, diff --git a/src/compiler.cr b/src/compiler.cr index 0b54af9c0..f66cb7235 100644 --- a/src/compiler.cr +++ b/src/compiler.cr @@ -1,7 +1,7 @@ module Mint class Compiler delegate html_elements, lookups, checked, cache, to: @artifacts - delegate ast, types, variables, to: @artifacts + delegate ast, types, variables, style_lookups, to: @artifacts delegate record_field_lookup, to: @artifacts getter js, style_builder diff --git a/src/compilers/html_element.cr b/src/compilers/html_element.cr index 1a3e1cc1e..abf66aba9 100644 --- a/src/compilers/html_element.cr +++ b/src/compilers/html_element.cr @@ -43,12 +43,16 @@ module Mint component = html_elements[node]? - style_node = - node.style && component && lookups[node] + style_nodes = + if node.styles && component + style_lookups[node] + end class_name = - if style_node - style_builder.style_pool.of(style_node, nil) + if style_nodes + style_nodes.map do |style_node| + style_builder.style_pool.of(style_node, nil) + end.join(" ") end class_name_attribute = @@ -79,7 +83,12 @@ module Mint styles = [] of String - styles << "this._#{class_name}()" if style_builder.any?(style_node) + if style_nodes + style_nodes.each do |style_node| + styles << "this._#{class_name}()" if style_builder.any?(style_node) + end + end + styles << custom_styles if custom_styles if styles.any? diff --git a/src/formatters/html_element.cr b/src/formatters/html_element.cr index 63f6e2c0b..0e512c9d3 100644 --- a/src/formatters/html_element.cr +++ b/src/formatters/html_element.cr @@ -4,12 +4,12 @@ module Mint tag = format node.tag - style = - format node.style + styles = + format node.styles prefix = - if style - "#{tag}::#{style}" + if styles + "#{tag}::#{styles.join("::")}" else tag end diff --git a/src/parsers/html_element.cr b/src/parsers/html_element.cr index 452aff9b2..0b6520e1a 100644 --- a/src/parsers/html_element.cr +++ b/src/parsers/html_element.cr @@ -16,7 +16,22 @@ module Mint skip unless tag if keyword "::" - style = variable_with_dashes! HtmlElementExpectedStyle + styles = [] of Ast::Variable + + many(parse_whitespace: false) do + if styles.any? + if keyword_ahead "::" + keyword "::" + else + break + end + end + + style = variable_with_dashes + styles << style if style + end + + raise HtmlElementExpectedStyle if styles.empty? end ref = start do @@ -37,7 +52,7 @@ module Mint from: start_position, children: children, comments: comments, - style: style, + styles: styles, to: position, input: data, tag: tag, diff --git a/src/type_checker.cr b/src/type_checker.cr index fb2d38d3e..80dca694e 100644 --- a/src/type_checker.cr +++ b/src/type_checker.cr @@ -30,7 +30,7 @@ module Mint property checking : Bool = true delegate types, variables, html_elements, ast, lookups, cache, to: artifacts - delegate checked, record_field_lookup, to: artifacts + delegate checked, record_field_lookup, style_lookups, to: artifacts delegate component?, component, stateful?, to: scope delegate format, to: formatter diff --git a/src/type_checkers/artifacts.cr b/src/type_checkers/artifacts.cr index 86670b3a8..25215bf47 100644 --- a/src/type_checkers/artifacts.cr +++ b/src/type_checkers/artifacts.cr @@ -1,11 +1,12 @@ module Mint class TypeChecker class Artifacts - getter types, variables, html_elements + getter types, variables, html_elements, style_lookups getter ast, lookups, cache, checked, record_field_lookup def initialize(@ast : Ast, @html_elements = {} of Ast::HtmlElement => Ast::Component | Nil, + @style_lookups = {} of Ast::Node => Array(Ast::Style), @record_field_lookup = {} of Ast::Node => String, @variables = {} of Ast::Node => Scope::Lookup, @lookups = {} of Ast::Node => Ast::Node, diff --git a/src/type_checkers/html_element.cr b/src/type_checkers/html_element.cr index 83216736e..622bcb8e5 100644 --- a/src/type_checkers/html_element.cr +++ b/src/type_checkers/html_element.cr @@ -5,25 +5,30 @@ module Mint type_error HtmlElementNotFoundStyle def check(node : Ast::HtmlElement) : Checkable - style = - node.style + styles = + node.styles - if style + if styles raise HtmlElementStyleOutsideOfComponent, { - "node" => style, + "node" => styles.map(&.value).join("::"), } unless component? - style_node = - component.styles.find(&.name.value.==(style.value)) + style_nodes = + styles.map do |style| + style_node = + component.styles.find(&.name.value.==(style.value)) - raise HtmlElementNotFoundStyle, { - "style" => style.value, - "node" => style, - } unless style_node + raise HtmlElementNotFoundStyle, { + "style" => style.value, + "node" => style, + } unless style_node - resolve style_node + resolve style_node - lookups[node] = style_node + style_node + end + + style_lookups[node] = style_nodes html_elements[node] = component end From dc49c0d209ba3a52b65d8f9f859f6b076a3ee549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guszt=C3=A1v=20Szikszai?= Date: Fri, 25 Oct 2019 09:32:09 +0200 Subject: [PATCH 12/21] Implemented case for css. --- spec/compilers/css_with_case | 54 ++++++++++++++++++++++++++++++++ src/ast/case_branch.cr | 2 +- src/compilers/case.cr | 14 +++++++-- src/compilers/case_branch.cr | 44 +++++++++++++++++++++----- src/formatters/case_branch.cr | 9 +++++- src/parsers/case.cr | 4 +-- src/parsers/case_branch.cr | 11 +++++-- src/parsers/style.cr | 9 +++++- src/style_builder.cr | 35 +++++++++++++++++++-- src/type_checkers/case_branch.cr | 16 ++++++++-- 10 files changed, 176 insertions(+), 22 deletions(-) create mode 100644 spec/compilers/css_with_case diff --git a/spec/compilers/css_with_case b/spec/compilers/css_with_case new file mode 100644 index 000000000..ffdf80935 --- /dev/null +++ b/spec/compilers/css_with_case @@ -0,0 +1,54 @@ +component Main { + style test { + color: yellow; + + case ("a") { + "a" => + color: red; + + => + color: blue; + } + } + + fun render : Html { + + } +} +-------------------------------------------------------------------------------- +class A extends _C { + _a() { + const _ = {} + + (() => { + let a = `a` + + if (_compare(--a-a, `a`)) { + Object.assign(_, { + [`--a-a`]: `red` + }) + } else { + Object.assign(_, { + [`--a-a`]: `blue` + }) + } + })() + + return _ + } + + render() { + return _h("div", { + className: `a`, + style: _style([this._a()]) + }) + } +} + +A.displayName = "Main" + +_insertStyles(` +.a { + color: var(--a-a, yellow); +} +`) diff --git a/src/ast/case_branch.cr b/src/ast/case_branch.cr index 1700727cc..bbab8560b 100644 --- a/src/ast/case_branch.cr +++ b/src/ast/case_branch.cr @@ -3,7 +3,7 @@ module Mint class CaseBranch < Node getter match, expression - def initialize(@expression : Expression, + def initialize(@expression : Node | Array(CssDefinition), @match : Node | Nil, @input : Data, @from : Int32, diff --git a/src/compilers/case.cr b/src/compilers/case.cr index 2df81ff52..4a155c906 100644 --- a/src/compilers/case.cr +++ b/src/compilers/case.cr @@ -1,6 +1,14 @@ module Mint class Compiler - def _compile(node : Ast::Case) : String + def compile(node : Ast::Case, block : Proc(String, String) | Nil = nil) : String + if checked.includes?(node) + _compile node, block + else + "" + end + end + + def _compile(node : Ast::Case, block : Proc(String, String) | Nil = nil) : String condition = compile node.condition @@ -11,7 +19,9 @@ module Mint node .branches .sort_by(&.match.nil?.to_s) - .map_with_index { |branch, index| compile branch, index, variable } + .map_with_index do |branch, index| + compile branch, index, variable, block + end js.iif do js.statements([condition_let, js.ifchain(body)]) diff --git a/src/compilers/case_branch.cr b/src/compilers/case_branch.cr index 656bb4647..93a84ab3f 100644 --- a/src/compilers/case_branch.cr +++ b/src/compilers/case_branch.cr @@ -1,16 +1,44 @@ module Mint class Compiler - def compile(node : Ast::CaseBranch, index : Int32, variable : String) : String + def compile(node : Ast::CaseBranch, + index : Int32, + variable : String, + block : Proc(String, String) | Nil = nil) : String if checked.includes?(node) - _compile node, index, variable + _compile node, index, variable, block else "" end end - def _compile(node : Ast::CaseBranch, index : Int32, variable : String) : String + def _compile(node : Ast::CaseBranch, + index : Int32, + variable : String, + block : Proc(String, String) | Nil = nil) : String expression = - compile node.expression + case item = node.expression + when Array(Ast::CssDefinition) + compiled = + item.each_with_object({} of String => String) do |definition, memo| + variable = + if block + block.call(definition.name) + else + "" + end + + value = + compile definition.value + + memo["[`#{variable}`]"] = "`#{value}`" + end + + "Object.assign(_, #{js.object(compiled)})" + when Ast::Node + js.return(compile(item)) + else + "" + end if match = node.match case match @@ -24,7 +52,7 @@ module Mint js.class_of(lookups[match]) js.if("#{variable} instanceof #{name}") do - js.statements(variables + [js.return(expression)]) + js.statements(variables + [expression]) end else compiled = @@ -32,17 +60,17 @@ module Mint if index == 0 js.if("_compare(#{variable}, #{compiled})") do - js.return(expression) + expression end else js.elseif("_compare(#{variable}, #{compiled})") do - js.return(expression) + expression end end end else js.else do - js.return(expression) + expression end end end diff --git a/src/formatters/case_branch.cr b/src/formatters/case_branch.cr index 1e86a0937..257b671c6 100644 --- a/src/formatters/case_branch.cr +++ b/src/formatters/case_branch.cr @@ -2,7 +2,14 @@ module Mint class Formatter def format(node : Ast::CaseBranch) : String expression = - format node.expression + case item = node.expression + when Array(Ast::CssDefinition) + list item + when Ast::Node + format item + else + "" + end match = format node.match diff --git a/src/parsers/case.cr b/src/parsers/case.cr index 950b983aa..caf3727a5 100644 --- a/src/parsers/case.cr +++ b/src/parsers/case.cr @@ -7,7 +7,7 @@ module Mint syntax_error CaseExpectedCondition syntax_error CaseExpectedBranches - def case_expression : Ast::Case | Nil + def case_expression(for_css : Bool = false) : Ast::Case | Nil start do |start_position| skip unless keyword "case" @@ -25,7 +25,7 @@ module Mint opening_bracket: CaseExpectedOpeningBracket, closing_bracket: CaseExpectedClosingBracket ) do - items = many { case_branch || comment }.compact + items = many { case_branch(for_css) || comment }.compact raise CaseExpectedBranches if items.empty? items end diff --git a/src/parsers/case_branch.cr b/src/parsers/case_branch.cr index 59632554c..70be7257e 100644 --- a/src/parsers/case_branch.cr +++ b/src/parsers/case_branch.cr @@ -2,7 +2,7 @@ module Mint class Parser syntax_error CaseBranchExpectedExpression - def case_branch : Ast::CaseBranch | Nil + def case_branch(for_css : Bool = false) : Ast::CaseBranch | Nil start do |start_position| unless keyword "=>" match = enum_destructuring || expression @@ -12,11 +12,16 @@ module Mint whitespace - raise CaseBranchExpectedExpression unless expression = self.expression + expression = + if for_css + many { css_definition }.compact + else + self.expression! CaseBranchExpectedExpression + end Ast::CaseBranch.new( match: match.as(Ast::EnumDestructuring | Ast::Expression | Nil), - expression: expression.as(Ast::Expression), + expression: expression, from: start_position, to: position, input: data) diff --git a/src/parsers/style.cr b/src/parsers/style.cr index 51c586a0e..418c5619a 100644 --- a/src/parsers/style.cr +++ b/src/parsers/style.cr @@ -29,7 +29,14 @@ module Mint end def css_body - many { comment || css_definition || css_if_expression || css_media || css_selector }.compact + many { + comment || + css_definition || + css_if_expression || + case_expression(for_css: true) || + css_media || + css_selector + }.compact end end end diff --git a/src/style_builder.cr b/src/style_builder.cr index 5f807a8a7..c515387e5 100644 --- a/src/style_builder.cr +++ b/src/style_builder.cr @@ -34,7 +34,7 @@ module Mint # Compiles the variables of a style node into a computed property. class VariableCompiler - delegate ifs, variables, variable_name, any?, style_pool, to: @builder + delegate ifs, variables, variable_name, any?, style_pool, cases, to: @builder delegate js, compile, to: @compiler getter compiler : Compiler @@ -98,10 +98,34 @@ module Mint js.statements(statements) end + compiled_cases = + cases + .select(&.first.==(node)) + .map do |(_, selector), selector_cases| + statements = + selector_cases.map do |item| + proc = + (Proc(String, String).new { |name| + variable = + variable_name name, selector + + selector[name] ||= PropertyValue.new + selector[name].variable = variable + + variable + }).as(Proc(String, String) | Nil) + + compiler.compile item, proc + end + + js.statements(statements) + end + js.function("_" + style_pool.of(node, nil), [] of String, js.statements([[ js.const("_", static), compiled_ifs, + compiled_cases, js.return("_"), ]].flatten.reject(&.empty?))) end @@ -135,6 +159,7 @@ module Mint alias Selector = Hash(String, PropertyValue) getter selectors, property_pool, name_pool, style_pool, variables, ifs + getter cases def initialize # Three name pools so there would be no clashes, @@ -156,6 +181,7 @@ module Mint # be compiled by the compiler itself when compiling an HTML element # which uses the specific style tag. @variables = {} of Ast::Node => Hash(String, Array(String | Ast::CssInterpolation)) + @cases = {} of Tuple(Ast::Node, Selector) => Array(Ast::Case) @ifs = {} of Tuple(Ast::Node, Selector) => Array(Ast::If) end @@ -194,7 +220,9 @@ module Mint end def any?(node : Ast::Node) - variables[node]? || ifs.any?(&.first.first.==(node)) + variables[node]? || + ifs.any?(&.first.first.==(node)) || + cases.any?(&.first.first.==(node)) end def any?(node : Nil) @@ -264,6 +292,9 @@ module Mint process(item, selectors, media, style_node) when Ast::CssMedia process(item, selectors, media, style_node) + when Ast::Case + cases[{style_node, selector}] ||= [] of Ast::Case + cases[{style_node, selector}] << item when Ast::If ifs[{style_node, selector}] ||= [] of Ast::If ifs[{style_node, selector}] << item diff --git a/src/type_checkers/case_branch.cr b/src/type_checkers/case_branch.cr index a00b439b4..0b77f3468 100644 --- a/src/type_checkers/case_branch.cr +++ b/src/type_checkers/case_branch.cr @@ -3,6 +3,18 @@ module Mint type_error CaseBranchNotMatchCondition def check(node : Ast::CaseBranch, condition : Checkable) : Checkable + resolve_expression = ->{ + case expression = node.expression + when Array(Ast::CssDefinition) + resolve expression + NEVER + when Ast::Node + resolve expression + else + NEVER + end + } + node.match.try do |item| match = resolve item @@ -38,10 +50,10 @@ module Mint end scope(variables) do - resolve node.expression + resolve_expression.call end end - end || resolve node.expression + end || resolve_expression.call end end end From 380275ac639a73cb431d7d971d13419f803b65ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guszt=C3=A1v=20Szikszai?= Date: Fri, 25 Oct 2019 09:51:32 +0200 Subject: [PATCH 13/21] Unify compiling of dynamic CSS definitions. --- spec/compilers/css_media_with_if | 8 ++--- spec/compilers/css_with_case | 2 +- spec/compilers/css_with_if | 14 +++----- src/ast/if.cr | 6 ++-- src/compilers/case_branch.cr | 18 +++------- src/compilers/if.cr | 45 +++++++++++++++++++++-- src/parsers/if.cr | 16 +++------ src/parsers/style.cr | 2 +- src/style_builder.cr | 61 +++++++------------------------- 9 files changed, 75 insertions(+), 97 deletions(-) diff --git a/spec/compilers/css_media_with_if b/spec/compilers/css_media_with_if index 5fd28771c..cebb385f2 100644 --- a/spec/compilers/css_media_with_if +++ b/spec/compilers/css_media_with_if @@ -18,11 +18,9 @@ class A extends _C { _a() { const _ = {} - if (true) { - Object.assign(_, { - [`--a-a`]: `red` - }) - } + (true ? Object.assign(_, { + [`--a-a`]: `red` + }) : null) return _ } diff --git a/spec/compilers/css_with_case b/spec/compilers/css_with_case index ffdf80935..e881ccb14 100644 --- a/spec/compilers/css_with_case +++ b/spec/compilers/css_with_case @@ -23,7 +23,7 @@ class A extends _C { (() => { let a = `a` - if (_compare(--a-a, `a`)) { + if (_compare(a, `a`)) { Object.assign(_, { [`--a-a`]: `red` }) diff --git a/spec/compilers/css_with_if b/spec/compilers/css_with_if index f3bac2c42..86c645e94 100644 --- a/spec/compilers/css_with_if +++ b/spec/compilers/css_with_if @@ -18,15 +18,11 @@ class A extends _C { _a() { const _ = {} - if (true) { - Object.assign(_, { - [`--a-a`]: `red` - }) - } else { - Object.assign(_, { - [`--a-a`]: `blue` - }) - } + (true ? Object.assign(_, { + [`--a-a`]: `red` + }) : Object.assign(_, { + [`--a-a`]: `blue` + })) return _ } diff --git a/src/ast/if.cr b/src/ast/if.cr index bbd750d1b..b52d45593 100644 --- a/src/ast/if.cr +++ b/src/ast/if.cr @@ -5,9 +5,9 @@ module Mint getter falsy_head_comments, falsy_tail_comments getter condition, branches - alias Branches = Tuple(Array(Node), Array(Node)) | - Tuple(Array(Node), Nil) | - Tuple(Array(Node), If) | + alias Branches = Tuple(Array(CssDefinition), Array(CssDefinition)) | + Tuple(Array(CssDefinition), Nil) | + Tuple(Array(CssDefinition), If) | Tuple(Node, Node) def initialize(@truthy_head_comments : Array(Comment), diff --git a/src/compilers/case_branch.cr b/src/compilers/case_branch.cr index 93a84ab3f..75cab3c47 100644 --- a/src/compilers/case_branch.cr +++ b/src/compilers/case_branch.cr @@ -19,21 +19,11 @@ module Mint case item = node.expression when Array(Ast::CssDefinition) compiled = - item.each_with_object({} of String => String) do |definition, memo| - variable = - if block - block.call(definition.name) - else - "" - end - - value = - compile definition.value - - memo["[`#{variable}`]"] = "`#{value}`" + if block + _compile item, block + else + "{}" end - - "Object.assign(_, #{js.object(compiled)})" when Ast::Node js.return(compile(item)) else diff --git a/src/compilers/if.cr b/src/compilers/if.cr index 408f3607f..9d6ba17db 100644 --- a/src/compilers/if.cr +++ b/src/compilers/if.cr @@ -1,6 +1,33 @@ module Mint class Compiler - def _compile(node : Ast::If) : String + def _compile(items : Array(Ast::CssDefinition), block : Proc(String, String) | Nil) + compiled = + items.each_with_object({} of String => String) do |definition, memo| + variable = + if block + block.call(definition.name) + else + "" + end + + value = + compile definition.value + + memo["[`#{variable}`]"] = "`#{value}`" + end + + "Object.assign(_, #{js.object(compiled)})" + end + + def compile(node : Ast::If, block : Proc(String, String) | Nil = nil) : String + if checked.includes?(node) + _compile node, block + else + "" + end + end + + def _compile(node : Ast::If, block : Proc(String, String) | Nil = nil) : String condition = compile node.condition @@ -8,10 +35,22 @@ module Mint node.branches truthy = - compile truthy_item + case item = truthy_item + when Array(Ast::CssDefinition) + _compile item, block: block + else + compile item + end falsy = - falsy_item.try { |item| compile item } + case item = falsy_item + when Array(Ast::CssDefinition) + _compile item, block: block + when Ast::Node + compile item + else + "null" + end "(#{condition} ? #{truthy} : #{falsy})" end diff --git a/src/parsers/if.cr b/src/parsers/if.cr index e3f61bcd4..7a2c0ad30 100644 --- a/src/parsers/if.cr +++ b/src/parsers/if.cr @@ -11,15 +11,7 @@ module Mint syntax_error IfExpectedCondition syntax_error IfExpectedElse - def if_expression - if_expression { } - end - - def css_if_expression - if_expression(true) { css_definition } - end - - def if_expression(for_css = false, &block : -> Ast::Node | Nil) : Ast::If | Nil + def if_expression(for_css = false) : Ast::If | Nil start do |start_position| skip unless keyword "if" @@ -36,7 +28,7 @@ module Mint closing_bracket: IfExpectedTruthyClosingBracket ) do if for_css - many { block.call }.compact + many { css_definition }.compact else expression! IfExpectedTruthyExpression end @@ -52,14 +44,14 @@ module Mint keyword! "else", IfExpectedElse whitespace - unless falsy = if_expression + unless falsy = if_expression(for_css) falsy_head_comments, falsy, falsy_tail_comments = block_with_comments( opening_bracket: IfExpectedFalsyOpeningBracket, closing_bracket: IfExpectedFalsyClosingBracket ) do if for_css - many { block.call }.compact + many { css_definition }.compact else expression! IfExpectedFalsyExpression end diff --git a/src/parsers/style.cr b/src/parsers/style.cr index 418c5619a..d2c475ecf 100644 --- a/src/parsers/style.cr +++ b/src/parsers/style.cr @@ -32,8 +32,8 @@ module Mint many { comment || css_definition || - css_if_expression || case_expression(for_css: true) || + if_expression(for_css: true) || css_media || css_selector }.compact diff --git a/src/style_builder.cr b/src/style_builder.cr index c515387e5..0ce0e0cb0 100644 --- a/src/style_builder.cr +++ b/src/style_builder.cr @@ -58,52 +58,13 @@ module Mint js.object(items) unless items.empty? end || "{}" - compiled_ifs = + compiled_conditions = ifs .select(&.first.==(node)) - .map do |(_, selector), selector_ifs| + .merge(cases.select(&.first.==(node))) + .map do |(_, selector), conditions| statements = - selector_ifs.map do |item| - condition = - compile item.condition - - thruty, falsy = - item.branches - - thruty_items = - case thruty - when Array(Ast::Node) - compile_branch thruty, selector - end - - falsy_items = - case falsy - when Array(Ast::Node) - compile_branch falsy, selector - end - - thruty_branch = - js.if(condition, "Object.assign(_, #{thruty_items})") - - falsy_branch = - if falsy_items - js.else { "Object.assign(_, #{falsy_items})" } - end - - [thruty_branch, falsy_branch] - .compact - .join(" ") - end - - js.statements(statements) - end - - compiled_cases = - cases - .select(&.first.==(node)) - .map do |(_, selector), selector_cases| - statements = - selector_cases.map do |item| + conditions.map do |item| proc = (Proc(String, String).new { |name| variable = @@ -115,7 +76,12 @@ module Mint variable }).as(Proc(String, String) | Nil) - compiler.compile item, proc + case item + when Ast::If, Ast::Case + compile item, proc + else + "" + end end js.statements(statements) @@ -124,17 +90,14 @@ module Mint js.function("_" + style_pool.of(node, nil), [] of String, js.statements([[ js.const("_", static), - compiled_ifs, - compiled_cases, + compiled_conditions, js.return("_"), ]].flatten.reject(&.empty?))) end - def compile_branch(items : Array(Ast::Node), selector : StyleBuilder::Selector) + def compile_branch(items : Array(Ast::CssDefinition), selector : StyleBuilder::Selector) compiled = items - .select(&.is_a?(Ast::CssDefinition)) - .map(&.as(Ast::CssDefinition)) .each_with_object({} of String => String) do |definition, memo| variable = variable_name definition.name, selector From cfbd87d5e93583878127349f9bd87a93b43753f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guszt=C3=A1v=20Szikszai?= Date: Fri, 25 Oct 2019 10:42:16 +0200 Subject: [PATCH 14/21] Allow arguments on styles. --- spec/formatters/css_with_arguments | 18 +++++++++++ spec/formatters/css_with_case | 31 +++++++++++++++++++ spec/formatters/css_with_if | 23 ++++++++++++++ spec/formatters/css_with_if_and_else | 25 +++++++++++++++ spec/parsers/style_spec.cr | 2 ++ spec/type_checking/css_with_arguments | 19 ++++++++++++ src/ast/style.cr | 5 +-- src/formatters/if.cr | 12 +++---- src/formatters/style.cr | 16 +++++++++- .../style_expected_closing_parentheses.cr | 15 +++++++++ src/parsers/style.cr | 18 ++++++++++- src/type_checkers/scope.cr | 7 +++++ src/type_checkers/style.cr | 7 +++-- 13 files changed, 185 insertions(+), 13 deletions(-) create mode 100644 spec/formatters/css_with_arguments create mode 100644 spec/formatters/css_with_case create mode 100644 spec/formatters/css_with_if create mode 100644 spec/formatters/css_with_if_and_else create mode 100644 spec/type_checking/css_with_arguments create mode 100644 src/messages/style_expected_closing_parentheses.cr diff --git a/spec/formatters/css_with_arguments b/spec/formatters/css_with_arguments new file mode 100644 index 000000000..84184ded0 --- /dev/null +++ b/spec/formatters/css_with_arguments @@ -0,0 +1,18 @@ +component A { + styletest(name:String){background: red;color: blue;} + + fun render : Html { +
+ } +} +-------------------------------------------------------------------------------- +component A { + style test (name : String) { + background: red; + color: blue; + } + + fun render : Html { +
+ } +} diff --git a/spec/formatters/css_with_case b/spec/formatters/css_with_case new file mode 100644 index 000000000..d77f77f8f --- /dev/null +++ b/spec/formatters/css_with_case @@ -0,0 +1,31 @@ +component A { + style test { + case (true) { + false => + background: red; + color: blue; + + true => color: red; + } + } + + fun render : Html { +
+ } +} +-------------------------------------------------------------------------------- +component A { + style test { + case (true) { + false => + background: red; + color: blue; + + true => color: red; + } + } + + fun render : Html { +
+ } +} diff --git a/spec/formatters/css_with_if b/spec/formatters/css_with_if new file mode 100644 index 000000000..b2a627349 --- /dev/null +++ b/spec/formatters/css_with_if @@ -0,0 +1,23 @@ +component A { + styletest{background: red;color: blue; + if(true){color:yellow;}} + + fun render : Html { +
+ } +} +-------------------------------------------------------------------------------- +component A { + style test { + background: red; + color: blue; + + if (true) { + color: yellow; + } + } + + fun render : Html { +
+ } +} diff --git a/spec/formatters/css_with_if_and_else b/spec/formatters/css_with_if_and_else new file mode 100644 index 000000000..35f7465c7 --- /dev/null +++ b/spec/formatters/css_with_if_and_else @@ -0,0 +1,25 @@ +component A { + styletest{background: red;color: blue; + if(true){color:yellow;}else{color:brown;}} + + fun render : Html { +
+ } +} +-------------------------------------------------------------------------------- +component A { + style test { + background: red; + color: blue; + + if (true) { + color: yellow; + } else { + color: brown; + } + } + + fun render : Html { +
+ } +} diff --git a/spec/parsers/style_spec.cr b/spec/parsers/style_spec.cr index 3064c9ef5..c14ff578c 100644 --- a/spec/parsers/style_spec.cr +++ b/spec/parsers/style_spec.cr @@ -15,6 +15,8 @@ describe "Component Style" do expect_error "style t {", Mint::Parser::StyleExpectedClosingBracket expect_error "style t { a: b;", Mint::Parser::StyleExpectedClosingBracket expect_error "style t { a: b; ", Mint::Parser::StyleExpectedClosingBracket + expect_error "style t (name : String", Mint::Parser::StyleExpectedClosingParentheses expect_ok "style t { a: b; }" + expect_ok "style t (name : String) { a: name; }" end diff --git a/spec/type_checking/css_with_arguments b/spec/type_checking/css_with_arguments new file mode 100644 index 000000000..5dba13a8a --- /dev/null +++ b/spec/type_checking/css_with_arguments @@ -0,0 +1,19 @@ +component Main { + style test(name : String) { + color: {name}; + } + + fun render : Html { + + } +} +-----------------------------------------------------------------VariableMissing +component Main { + style test(name : String) { + color: {name2}; + } + + fun render : Html { + + } +} diff --git a/src/ast/style.cr b/src/ast/style.cr index bb03468c2..6044a2f35 100644 --- a/src/ast/style.cr +++ b/src/ast/style.cr @@ -1,9 +1,10 @@ module Mint class Ast class Style < Node - getter name, body + getter name, body, arguments - def initialize(@body : Array(Node), + def initialize(@arguments : Array(Argument), + @body : Array(Node), @name : Variable, @input : Data, @from : Int32, diff --git a/src/formatters/if.cr b/src/formatters/if.cr index 4e6616d91..1cbd7b50f 100644 --- a/src/formatters/if.cr +++ b/src/formatters/if.cr @@ -9,7 +9,7 @@ module Mint truthy = case truthy_item - when Array(Ast::Node) + when Array(Ast::CssDefinition) list truthy_item + node.truthy_head_comments + node.truthy_tail_comments when Ast::Node list [truthy_item] + node.truthy_head_comments + node.truthy_tail_comments @@ -21,19 +21,17 @@ module Mint if falsy_item.is_a?(Ast::If) && node.falsy_head_comments.empty? && node.falsy_tail_comments.empty? - format falsy_item + " else " + format(falsy_item) else body = case falsy_item - when Array(Ast::Node) + when Array(Ast::CssDefinition) list falsy_item + node.falsy_head_comments + node.falsy_tail_comments when Ast::Node list [falsy_item] + node.falsy_head_comments + node.falsy_tail_comments - else - "" end - "{\n#{indent(body)}\n}" + " else {\n#{indent(body)}\n}" if body end condition = @@ -46,7 +44,7 @@ module Mint condition end - "if (#{condition}) {\n#{indent(truthy)}\n} else #{falsy}" + "if (#{condition}) {\n#{indent(truthy)}\n}#{falsy}" end end end diff --git a/src/formatters/style.cr b/src/formatters/style.cr index 0e638bf14..f07a64844 100644 --- a/src/formatters/style.cr +++ b/src/formatters/style.cr @@ -7,7 +7,21 @@ module Mint body = list node.body - "style #{name} {\n#{indent(body)}\n}" + arguments = + unless node.arguments.empty? + value = + format node.arguments + + if value + .map { |string| replace_skipped(string) } + .map(&.size).sum > 50 + "(\n#{indent(value.join(",\n"))}\n) " + else + "(#{value.join(", ")}) " + end + end + + "style #{name} #{arguments}{\n#{indent(body)}\n}" end end end diff --git a/src/messages/style_expected_closing_parentheses.cr b/src/messages/style_expected_closing_parentheses.cr new file mode 100644 index 000000000..6e1b59ab3 --- /dev/null +++ b/src/messages/style_expected_closing_parentheses.cr @@ -0,0 +1,15 @@ +message StyleExpectedClosingParentheses do + title "Syntax Error" + + block do + text "The" + bold "arguments" + text "of a" + bold "style" + text "must be enclosed by parenthesis." + end + + was_looking_for "closing parenthesis", got, ")" + + snippet node +end diff --git a/src/parsers/style.cr b/src/parsers/style.cr index d2c475ecf..9dee9d85c 100644 --- a/src/parsers/style.cr +++ b/src/parsers/style.cr @@ -1,5 +1,6 @@ module Mint class Parser + syntax_error StyleExpectedClosingParentheses syntax_error StyleExpectedOpeningBracket syntax_error StyleExpectedClosingBracket syntax_error StyleExpectedName @@ -9,8 +10,22 @@ module Mint skip unless keyword "style" whitespace - name = variable_with_dashes! StyleExpectedName + whitespace + + arguments = [] of Ast::Argument + + if char! '(' + whitespace + + arguments.concat list( + terminator: ')', + separator: ',' + ) { argument }.compact + + whitespace + char ')', StyleExpectedClosingParentheses + end body = block( opening_bracket: StyleExpectedOpeningBracket, @@ -21,6 +36,7 @@ module Mint Ast::Style.new( from: start_position, + arguments: arguments, to: position, input: data, body: body, diff --git a/src/type_checkers/scope.cr b/src/type_checkers/scope.cr index 2ec111b46..46e93e06c 100644 --- a/src/type_checkers/scope.cr +++ b/src/type_checkers/scope.cr @@ -8,6 +8,7 @@ module Mint Ast::Provider | Ast::Module | Ast::Store | + Ast::Style | Ast::Get alias Level = Tuple(Ast::Node | Checkable, Node) @@ -38,6 +39,8 @@ module Mint node.name when Ast::Function node.name.value + when Ast::Style + node.name.value else "" # Cannot happen end @@ -107,6 +110,10 @@ module Mint node.where.try(&.statements.find(&.name.value.==(variable))) end + def find(variable : String, node : Ast::Style) + node.arguments.find(&.name.value.==(variable)) + end + def find(variable : String, node : Ast::Get) node.where.try(&.statements.find(&.name.value.==(variable))) end diff --git a/src/type_checkers/style.cr b/src/type_checkers/style.cr index 3f1298cc6..f8015107e 100644 --- a/src/type_checkers/style.cr +++ b/src/type_checkers/style.cr @@ -1,9 +1,12 @@ module Mint class TypeChecker def check(node : Ast::Style) : Checkable - resolve node.body + scope node do + resolve node.arguments + resolve node.body - NEVER + NEVER + end end end end From 605e3d5f514f2783367e93a9bb3abb24ceef5908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guszt=C3=A1v=20Szikszai?= Date: Fri, 25 Oct 2019 12:45:50 +0200 Subject: [PATCH 15/21] Implement type checking and compiling of style arguments. --- spec/compilers/css_with_arguments | 34 ++++++++++++++ spec/formatters/html_style | 20 ++++++++ spec/type_checking/css_with_arguments | 4 +- spec/type_checking/html_element_style | 2 +- spec/type_checking/html_style | 39 ++++++++++++++++ src/ast/html_element.cr | 2 +- src/ast/html_style.cr | 14 ++++++ src/compiler.cr | 4 +- src/compilers/html_element.cr | 18 ++++---- src/formatters/html_element.cr | 7 +-- src/formatters/html_style.cr | 20 ++++++++ .../html_style_argument_size_mismatch.cr | 12 +++++ .../html_style_argument_type_mismatch.cr | 14 ++++++ ...html_style_expected_closing_parentheses.cr | 15 ++++++ ...found_style.cr => html_style_not_found.cr} | 2 +- src/parser.cr | 1 + src/parsers/html_element.cr | 19 ++------ src/parsers/html_style.cr | 39 ++++++++++++++++ src/style_builder.cr | 5 +- src/type_checker.cr | 4 +- src/type_checkers/artifacts.cr | 4 +- src/type_checkers/html_element.cr | 26 ++--------- src/type_checkers/html_style.cr | 46 +++++++++++++++++++ 23 files changed, 286 insertions(+), 65 deletions(-) create mode 100644 spec/compilers/css_with_arguments create mode 100644 spec/formatters/html_style create mode 100644 spec/type_checking/html_style create mode 100644 src/ast/html_style.cr create mode 100644 src/formatters/html_style.cr create mode 100644 src/messages/html_style_argument_size_mismatch.cr create mode 100644 src/messages/html_style_argument_type_mismatch.cr create mode 100644 src/messages/html_style_expected_closing_parentheses.cr rename src/messages/{html_element_not_found_style.cr => html_style_not_found.cr} (81%) create mode 100644 src/parsers/html_style.cr create mode 100644 src/type_checkers/html_style.cr diff --git a/spec/compilers/css_with_arguments b/spec/compilers/css_with_arguments new file mode 100644 index 000000000..ff85120f8 --- /dev/null +++ b/spec/compilers/css_with_arguments @@ -0,0 +1,34 @@ +component Main { + style test(color : String) { + color: {color}; + } + + fun render : Html { + + } +} +-------------------------------------------------------------------------------- +class A extends _C { + _a(a) { + const _ = { + [`--a-a`]: a + } + + return _ + } + + render() { + return _h("div", { + className: `a`, + style: _style([this._a(`red`)]) + }) + } +} + +A.displayName = "Main" + +_insertStyles(` +.a { + color: var(--a-a); +} +`) diff --git a/spec/formatters/html_style b/spec/formatters/html_style new file mode 100644 index 000000000..9648108ad --- /dev/null +++ b/spec/formatters/html_style @@ -0,0 +1,20 @@ +component Test { + style base(name : String, active : Bool) { + color: red; + } + + fun render : Html { + +
+ } +} +-------------------------------------------------------------------------------- +component Test { + style base (name : String, active : Bool) { + color: red; + } + + fun render : Html { + + } +} diff --git a/spec/type_checking/css_with_arguments b/spec/type_checking/css_with_arguments index 5dba13a8a..e2eb93354 100644 --- a/spec/type_checking/css_with_arguments +++ b/spec/type_checking/css_with_arguments @@ -4,7 +4,7 @@ component Main { } fun render : Html { - + } } -----------------------------------------------------------------VariableMissing @@ -14,6 +14,6 @@ component Main { } fun render : Html { - + } } diff --git a/spec/type_checking/html_element_style b/spec/type_checking/html_element_style index 4016536a2..ece1768f2 100644 --- a/spec/type_checking/html_element_style +++ b/spec/type_checking/html_element_style @@ -8,7 +8,7 @@ component Main {
} } ---------------------------------------------------------HtmlElementNotFoundStyle +---------------------------------------------------------------HtmlStyleNotFound component Main { fun render : Html { diff --git a/spec/type_checking/html_style b/spec/type_checking/html_style new file mode 100644 index 000000000..56578e702 --- /dev/null +++ b/spec/type_checking/html_style @@ -0,0 +1,39 @@ +component Main { + style base (name : String, active : Bool) { + color: red; + } + + fun render : Html { + + } +} +---------------------------------------------------HtmlStyleArgumentSizeMismatch +component Main { + style base (name : String, active : Bool) { + color: red; + } + + fun render : Html { + + } +} +---------------------------------------------------HtmlStyleArgumentSizeMismatch +component Main { + style base (name : String, active : Bool) { + color: red; + } + + fun render : Html { + + } +} +---------------------------------------------------HtmlStyleArgumentTypeMismatch +component Main { + style base (name : String, active : Bool) { + color: red; + } + + fun render : Html { + + } +} diff --git a/src/ast/html_element.cr b/src/ast/html_element.cr index 31c4e842f..9cd896ba7 100644 --- a/src/ast/html_element.cr +++ b/src/ast/html_element.cr @@ -4,9 +4,9 @@ module Mint getter attributes, children, styles, tag, comments, ref def initialize(@attributes : Array(HtmlAttribute), - @styles : Array(Variable) | Nil, @children : Array(HtmlContent), @comments : Array(Comment), + @styles : Array(HtmlStyle), @ref : Variable | Nil, @tag : Variable, @input : Data, diff --git a/src/ast/html_style.cr b/src/ast/html_style.cr new file mode 100644 index 000000000..114d6c78a --- /dev/null +++ b/src/ast/html_style.cr @@ -0,0 +1,14 @@ +module Mint + class Ast + class HtmlStyle < Node + getter name, arguments + + def initialize(@arguments : Array(Expression), + @name : Variable, + @input : Data, + @from : Int32, + @to : Int32) + end + end + end +end diff --git a/src/compiler.cr b/src/compiler.cr index f66cb7235..21f61ad4d 100644 --- a/src/compiler.cr +++ b/src/compiler.cr @@ -1,7 +1,7 @@ module Mint class Compiler - delegate html_elements, lookups, checked, cache, to: @artifacts - delegate ast, types, variables, style_lookups, to: @artifacts + delegate lookups, checked, cache, to: @artifacts + delegate ast, types, variables, to: @artifacts delegate record_field_lookup, to: @artifacts getter js, style_builder diff --git a/src/compilers/html_element.cr b/src/compilers/html_element.cr index abf66aba9..332a9033f 100644 --- a/src/compilers/html_element.cr +++ b/src/compilers/html_element.cr @@ -40,16 +40,11 @@ module Mint .map { |attribute| resolve(attribute) } .reduce({} of String => String) { |memo, item| memo.merge(item) } - component = - html_elements[node]? - style_nodes = - if node.styles && component - style_lookups[node] - end + node.styles.map { |item| lookups[item] } class_name = - if style_nodes + if style_nodes.any? style_nodes.map do |style_node| style_builder.style_pool.of(style_node, nil) end.join(" ") @@ -83,9 +78,12 @@ module Mint styles = [] of String - if style_nodes - style_nodes.each do |style_node| - styles << "this._#{class_name}()" if style_builder.any?(style_node) + node.styles.each do |item| + if style_builder.any?(lookups[item]) + arguments = + compile item.arguments + + styles << js.call("this._#{class_name}", arguments) end end diff --git a/src/formatters/html_element.cr b/src/formatters/html_element.cr index 0e512c9d3..2cbecbc40 100644 --- a/src/formatters/html_element.cr +++ b/src/formatters/html_element.cr @@ -4,12 +4,9 @@ module Mint tag = format node.tag - styles = - format node.styles - prefix = - if styles - "#{tag}::#{styles.join("::")}" + if styles = node.styles + "#{tag}#{format(styles, "")}" else tag end diff --git a/src/formatters/html_style.cr b/src/formatters/html_style.cr new file mode 100644 index 000000000..32e5f218e --- /dev/null +++ b/src/formatters/html_style.cr @@ -0,0 +1,20 @@ +module Mint + class Formatter + def format(node : Ast::HtmlStyle) : String + name = + node.name.value + + arguments = + if node.arguments.any? + items = + format node.arguments, ", " + + "(#{items})" + else + "" + end + + "::#{name}#{arguments}" + end + end +end diff --git a/src/messages/html_style_argument_size_mismatch.cr b/src/messages/html_style_argument_size_mismatch.cr new file mode 100644 index 000000000..aecfa09d5 --- /dev/null +++ b/src/messages/html_style_argument_size_mismatch.cr @@ -0,0 +1,12 @@ +message HtmlStyleArgumentSizeMismatch do + title "Type Error" + + block do + text "The style takes" + bold size + text "arguments, while you tried to call it with" + bold call_size + end + + snippet node, "You tried to call it here:" +end diff --git a/src/messages/html_style_argument_type_mismatch.cr b/src/messages/html_style_argument_type_mismatch.cr new file mode 100644 index 000000000..22dc51625 --- /dev/null +++ b/src/messages/html_style_argument_type_mismatch.cr @@ -0,0 +1,14 @@ +message HtmlStyleArgumentTypeMismatch do + title "Type Error" + + block do + text "The" + bold "#{index} argument" + text "to a style is causing a mismatch." + end + + type_with_text expected, "The style is expecting the #{index} argument to be:" + type_with_text got, "Instead it is:" + + snippet node, "You tried to call it here:" +end diff --git a/src/messages/html_style_expected_closing_parentheses.cr b/src/messages/html_style_expected_closing_parentheses.cr new file mode 100644 index 000000000..d80f7b092 --- /dev/null +++ b/src/messages/html_style_expected_closing_parentheses.cr @@ -0,0 +1,15 @@ +message HtmlStyleExpectedClosingParentheses do + title "Syntax Error" + + block do + text "The" + bold "arguments" + text "of a" + bold "style" + text "must be enclosed by parenthesis." + end + + was_looking_for "closing parenthesis", got, ")" + + snippet node +end diff --git a/src/messages/html_element_not_found_style.cr b/src/messages/html_style_not_found.cr similarity index 81% rename from src/messages/html_element_not_found_style.cr rename to src/messages/html_style_not_found.cr index c8432f47f..1bdc72a6f 100644 --- a/src/messages/html_element_not_found_style.cr +++ b/src/messages/html_style_not_found.cr @@ -1,4 +1,4 @@ -message HtmlElementNotFoundStyle do +message HtmlStyleNotFound do title "Type Error" block do diff --git a/src/parser.cr b/src/parser.cr index 7ed6b2ed2..549963ff6 100644 --- a/src/parser.cr +++ b/src/parser.cr @@ -143,6 +143,7 @@ module Mint def keyword(word) : Bool result = input[position, word.size] + if result == word @position += word.size true diff --git a/src/parsers/html_element.cr b/src/parsers/html_element.cr index 0b6520e1a..85e8f0fa7 100644 --- a/src/parsers/html_element.cr +++ b/src/parsers/html_element.cr @@ -15,21 +15,12 @@ module Mint skip unless tag - if keyword "::" - styles = [] of Ast::Variable + styles = [] of Ast::HtmlStyle - many(parse_whitespace: false) do - if styles.any? - if keyword_ahead "::" - keyword "::" - else - break - end - end - - style = variable_with_dashes - styles << style if style - end + if keyword_ahead "::" + styles.concat(many(parse_whitespace: false) do + html_style + end.compact) raise HtmlElementExpectedStyle if styles.empty? end diff --git a/src/parsers/html_style.cr b/src/parsers/html_style.cr new file mode 100644 index 000000000..aa3298f31 --- /dev/null +++ b/src/parsers/html_style.cr @@ -0,0 +1,39 @@ +module Mint + class Parser + syntax_error HtmlStyleExpectedClosingParentheses + + def html_style : Ast::HtmlStyle | Nil + start do |start_position| + name = start do + skip unless keyword "::" + skip unless value = variable_with_dashes + value + end + + skip unless name + + arguments = [] of Ast::Node + + if char! '(' + whitespace + + list(terminator: ')', separator: ',') do + if item = expression + arguments << item + end + end + + whitespace + char ')', HtmlStyleExpectedClosingParentheses + end + + Ast::HtmlStyle.new( + arguments: arguments.compact, + from: start_position, + to: position, + input: data, + name: name) + end + end + end +end diff --git a/src/style_builder.cr b/src/style_builder.cr index 0ce0e0cb0..907d33fdf 100644 --- a/src/style_builder.cr +++ b/src/style_builder.cr @@ -87,7 +87,10 @@ module Mint js.statements(statements) end - js.function("_" + style_pool.of(node, nil), [] of String, + arguments = + compile node.arguments + + js.function("_" + style_pool.of(node, nil), arguments, js.statements([[ js.const("_", static), compiled_conditions, diff --git a/src/type_checker.cr b/src/type_checker.cr index 80dca694e..8d42f2a3f 100644 --- a/src/type_checker.cr +++ b/src/type_checker.cr @@ -29,8 +29,8 @@ module Mint property checking : Bool = true - delegate types, variables, html_elements, ast, lookups, cache, to: artifacts - delegate checked, record_field_lookup, style_lookups, to: artifacts + delegate types, variables, ast, lookups, cache, to: artifacts + delegate checked, record_field_lookup, to: artifacts delegate component?, component, stateful?, to: scope delegate format, to: formatter diff --git a/src/type_checkers/artifacts.cr b/src/type_checkers/artifacts.cr index 25215bf47..3398b130f 100644 --- a/src/type_checkers/artifacts.cr +++ b/src/type_checkers/artifacts.cr @@ -1,12 +1,10 @@ module Mint class TypeChecker class Artifacts - getter types, variables, html_elements, style_lookups getter ast, lookups, cache, checked, record_field_lookup + getter types, variables def initialize(@ast : Ast, - @html_elements = {} of Ast::HtmlElement => Ast::Component | Nil, - @style_lookups = {} of Ast::Node => Array(Ast::Style), @record_field_lookup = {} of Ast::Node => String, @variables = {} of Ast::Node => Scope::Lookup, @lookups = {} of Ast::Node => Ast::Node, diff --git a/src/type_checkers/html_element.cr b/src/type_checkers/html_element.cr index 622bcb8e5..20bcfc0a6 100644 --- a/src/type_checkers/html_element.cr +++ b/src/type_checkers/html_element.cr @@ -2,34 +2,14 @@ module Mint class TypeChecker type_error HtmlElementReferenceOutsideOfComponent type_error HtmlElementStyleOutsideOfComponent - type_error HtmlElementNotFoundStyle def check(node : Ast::HtmlElement) : Checkable - styles = - node.styles - - if styles + if node.styles.any? raise HtmlElementStyleOutsideOfComponent, { - "node" => styles.map(&.value).join("::"), + "node" => node, } unless component? - style_nodes = - styles.map do |style| - style_node = - component.styles.find(&.name.value.==(style.value)) - - raise HtmlElementNotFoundStyle, { - "style" => style.value, - "node" => style, - } unless style_node - - resolve style_node - - style_node - end - - style_lookups[node] = style_nodes - html_elements[node] = component + resolve node.styles end node.ref.try do |ref| diff --git a/src/type_checkers/html_style.cr b/src/type_checkers/html_style.cr new file mode 100644 index 000000000..9e6035625 --- /dev/null +++ b/src/type_checkers/html_style.cr @@ -0,0 +1,46 @@ +module Mint + class TypeChecker + type_error HtmlStyleArgumentSizeMismatch + type_error HtmlStyleArgumentTypeMismatch + type_error HtmlStyleNotFound + + def check(node : Ast::HtmlStyle) : Checkable + style = + component.styles.find(&.name.value.==(node.name.value)) + + raise HtmlStyleNotFound, { + "style" => node.name.value, + "node" => node, + } unless style + + resolve style + + raise HtmlStyleArgumentSizeMismatch, { + "size" => style.arguments.size.to_s, + "call_size" => node.arguments.size.to_s, + "node" => node, + } unless style.arguments.size == node.arguments.size + + style.arguments + .zip(node.arguments) + .each_with_index do |(style_arg, call_arg), index| + style_arg_type = + resolve(style_arg) + + call_arg_type = + resolve(call_arg) + + raise HtmlStyleArgumentTypeMismatch, { + "index" => ordinal(index + 1), + "expected" => style_arg_type, + "got" => call_arg_type, + "node" => node, + } unless Comparer.compare(style_arg_type, call_arg_type) + end + + lookups[node] = style + + NEVER + end + end +end From 962d7c4bf33699a169b840fddde6d85aebc2b037 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guszt=C3=A1v=20Szikszai?= Date: Fri, 25 Oct 2019 15:09:38 +0200 Subject: [PATCH 16/21] Use $ instead of _ for style functions and use selector id instead of selector. --- spec/compilers/css_definition | 4 +-- spec/compilers/css_media | 4 +-- spec/compilers/css_media_with_if | 4 +-- spec/compilers/css_selector | 4 +-- spec/compilers/css_with_arguments | 4 +-- spec/compilers/css_with_case | 4 +-- spec/compilers/css_with_if | 4 +-- spec/compilers/html_with_pseudos | 6 ++-- spec/compilers/html_with_style | 29 +++++++++++++++---- .../html_with_style_and_custom_style | 4 +-- spec/name_pool_spec.cr | 13 +++++++++ src/compilers/html_element.cr | 2 +- src/compilers/top_level.cr | 4 +-- src/style_builder.cr | 14 +++++---- 14 files changed, 66 insertions(+), 34 deletions(-) create mode 100644 spec/name_pool_spec.cr diff --git a/spec/compilers/css_definition b/spec/compilers/css_definition index 4da680fde..4de4b44cf 100644 --- a/spec/compilers/css_definition +++ b/spec/compilers/css_definition @@ -13,7 +13,7 @@ component Main { } -------------------------------------------------------------------------------- class A extends _C { - _a() { + $a() { const _ = { [`--a-a`]: this.a + `px 0px` } @@ -28,7 +28,7 @@ class A extends _C { render() { return _h("div", { className: `a`, - style: _style([this._a()]) + style: _style([this.$a()]) }) } } diff --git a/spec/compilers/css_media b/spec/compilers/css_media index 435f7864f..53384dce7 100644 --- a/spec/compilers/css_media +++ b/spec/compilers/css_media @@ -19,7 +19,7 @@ component Main { } -------------------------------------------------------------------------------- class A extends _C { - _a() { + $a() { const _ = { [`--a-a`]: this.a, [`--b-a`]: this.a @@ -35,7 +35,7 @@ class A extends _C { render() { return _h("div", { className: `a`, - style: _style([this._a()]) + style: _style([this.$a()]) }) } } diff --git a/spec/compilers/css_media_with_if b/spec/compilers/css_media_with_if index cebb385f2..43645833b 100644 --- a/spec/compilers/css_media_with_if +++ b/spec/compilers/css_media_with_if @@ -15,7 +15,7 @@ component Main { } -------------------------------------------------------------------------------- class A extends _C { - _a() { + $a() { const _ = {} (true ? Object.assign(_, { @@ -28,7 +28,7 @@ class A extends _C { render() { return _h("div", { className: `a`, - style: _style([this._a()]) + style: _style([this.$a()]) }) } } diff --git a/spec/compilers/css_selector b/spec/compilers/css_selector index afb029b28..d54614dee 100644 --- a/spec/compilers/css_selector +++ b/spec/compilers/css_selector @@ -19,7 +19,7 @@ component Main { } -------------------------------------------------------------------------------- class A extends _C { - _a() { + $a() { const _ = { [`--a-a`]: this.a } @@ -34,7 +34,7 @@ class A extends _C { render() { return _h("div", { className: `a`, - style: _style([this._a()]) + style: _style([this.$a()]) }) } } diff --git a/spec/compilers/css_with_arguments b/spec/compilers/css_with_arguments index ff85120f8..d65037f42 100644 --- a/spec/compilers/css_with_arguments +++ b/spec/compilers/css_with_arguments @@ -9,7 +9,7 @@ component Main { } -------------------------------------------------------------------------------- class A extends _C { - _a(a) { + $a(a) { const _ = { [`--a-a`]: a } @@ -20,7 +20,7 @@ class A extends _C { render() { return _h("div", { className: `a`, - style: _style([this._a(`red`)]) + style: _style([this.$a(`red`)]) }) } } diff --git a/spec/compilers/css_with_case b/spec/compilers/css_with_case index e881ccb14..a2c95d459 100644 --- a/spec/compilers/css_with_case +++ b/spec/compilers/css_with_case @@ -17,7 +17,7 @@ component Main { } -------------------------------------------------------------------------------- class A extends _C { - _a() { + $a() { const _ = {} (() => { @@ -40,7 +40,7 @@ class A extends _C { render() { return _h("div", { className: `a`, - style: _style([this._a()]) + style: _style([this.$a()]) }) } } diff --git a/spec/compilers/css_with_if b/spec/compilers/css_with_if index 86c645e94..701aad866 100644 --- a/spec/compilers/css_with_if +++ b/spec/compilers/css_with_if @@ -15,7 +15,7 @@ component Main { } -------------------------------------------------------------------------------- class A extends _C { - _a() { + $a() { const _ = {} (true ? Object.assign(_, { @@ -30,7 +30,7 @@ class A extends _C { render() { return _h("div", { className: `a`, - style: _style([this._a()]) + style: _style([this.$a()]) }) } } diff --git a/spec/compilers/html_with_pseudos b/spec/compilers/html_with_pseudos index 2173041ce..131da6796 100644 --- a/spec/compilers/html_with_pseudos +++ b/spec/compilers/html_with_pseudos @@ -11,7 +11,7 @@ component Main { color: cyan; } - & div { + div { font-family: {"Hello"}; color: blue; } @@ -39,7 +39,7 @@ class A extends _C { }) } - _a() { + $a() { const _ = { [`--a-a`]: this.a, [`--b-a`]: this.b, @@ -52,7 +52,7 @@ class A extends _C { render() { return _h("div", { className: `a`, - style: _style([this._a()]) + style: _style([this.$a()]) }) } } diff --git a/spec/compilers/html_with_style b/spec/compilers/html_with_style index db4bab89f..5939a30db 100644 --- a/spec/compilers/html_with_style +++ b/spec/compilers/html_with_style @@ -1,9 +1,15 @@ component Main { property background : String = "blue" + property color : String = "yellow" style test { + -webkit-tap-highlight-color: rgba(0,0,0,0); + -webkit-touch-callout: none; + + border-color: {background}; background: {background}; - color: red; + border: {background}; + color: {color}; } fun render : Html { @@ -20,13 +26,20 @@ class A extends _C { a: [ null, `blue` + ], + b: [ + null, + `yellow` ] }) } - _a() { + $a() { const _ = { - [`--a-a`]: this.a + [`--a-a`]: this.a, + [`--a-b`]: this.a, + [`--a-c`]: this.a, + [`--a-d`]: this.b } return _ @@ -35,7 +48,7 @@ class A extends _C { render() { return _h("div", { className: `a`, - style: _style([this._a()]) + style: _style([this.$a()]) }) } } @@ -44,7 +57,11 @@ A.displayName = "Main" _insertStyles(` .a { - background: var(--a-a); - color: red; + -webkit-tap-highlight-color: rgba(0,0,0,0); + -webkit-touch-callout: none; + border-color: var(--a-a); + background: var(--a-b); + border: var(--a-c); + color: var(--a-d); } `) diff --git a/spec/compilers/html_with_style_and_custom_style b/spec/compilers/html_with_style_and_custom_style index f55d0fadb..d20b768ce 100644 --- a/spec/compilers/html_with_style_and_custom_style +++ b/spec/compilers/html_with_style_and_custom_style @@ -28,7 +28,7 @@ class A extends _C { }) } - _a() { + $a() { const _ = { [`--a-a`]: this.a } @@ -43,7 +43,7 @@ class A extends _C { render() { return _h("div", { className: `a`, - style: _style([this._a(), this.b]) + style: _style([this.$a(), this.b]) }) } } diff --git a/spec/name_pool_spec.cr b/spec/name_pool_spec.cr new file mode 100644 index 000000000..4edb89f60 --- /dev/null +++ b/spec/name_pool_spec.cr @@ -0,0 +1,13 @@ +require "./spec_helper" + +describe Mint::NamePool do + it "returns nex name" do + pool = Mint::NamePool(String, Mint::StyleBuilder::Selector).new + object = Mint::StyleBuilder::Selector.new + + pool.of("a", object).should eq("a") + pool.of("a", object).should eq("a") + pool.of("b", object).should eq("b") + pool.of("c", object).should eq("c") + end +end diff --git a/src/compilers/html_element.cr b/src/compilers/html_element.cr index 332a9033f..bf647ead5 100644 --- a/src/compilers/html_element.cr +++ b/src/compilers/html_element.cr @@ -83,7 +83,7 @@ module Mint arguments = compile item.arguments - styles << js.call("this._#{class_name}", arguments) + styles << js.call("this.$#{class_name}", arguments) end end diff --git a/src/compilers/top_level.cr b/src/compilers/top_level.cr index 2b397f35c..e748e9d68 100644 --- a/src/compilers/top_level.cr +++ b/src/compilers/top_level.cr @@ -184,10 +184,10 @@ module Mint const _E = mint.Enum; const _s = (item, callback) => { - if (item instanceof Nothing) { + if (item instanceof #{nothing}) { return item } else if (item instanceof #{just}) { - return new #{just}(callback(item.value)) + return new #{just}(callback(item._0)) } else { return callback(item) } diff --git a/src/style_builder.cr b/src/style_builder.cr index 907d33fdf..f09746e7a 100644 --- a/src/style_builder.cr +++ b/src/style_builder.cr @@ -90,7 +90,7 @@ module Mint arguments = compile node.arguments - js.function("_" + style_pool.of(node, nil), arguments, + js.function("$" + style_pool.of(node, nil), arguments, js.statements([[ js.const("_", static), compiled_conditions, @@ -122,7 +122,9 @@ module Mint # nested media queries and selectors, handling cases of the same rules in # different places. class StyleBuilder - alias Selector = Hash(String, PropertyValue) + class Selector < Hash(String, PropertyValue) + getter id : String = Random::Secure.hex + end getter selectors, property_pool, name_pool, style_pool, variables, ifs getter cases @@ -130,9 +132,9 @@ module Mint def initialize # Three name pools so there would be no clashes, # which also good for optimizations. - @property_pool = NamePool(String, Selector).new + @property_pool = NamePool(String, String).new @style_pool = NamePool(Ast::Node, Nil).new - @name_pool = NamePool(Selector, Nil).new + @name_pool = NamePool(String, Nil).new # This is the main data structure: # @@ -271,11 +273,11 @@ module Mint def variable_name(name, selector) # Get the unique ID of the selector block_id = - name_pool.of(selector, nil) + name_pool.of(selector.id, nil) # Get the unique ID of the property variable_id = - property_pool.of(name, selector) + property_pool.of(name, selector.id) "--#{block_id}-#{variable_id}" end From 0770911d51ca910a90b5f86e92cd89ad37607443 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guszt=C3=A1v=20Szikszai?= Date: Fri, 25 Oct 2019 15:41:13 +0200 Subject: [PATCH 17/21] Use "#{...}" for CSS interpolaction instealof "{...}" --- spec/compilers/css_definition | 2 +- spec/compilers/css_media | 4 ++-- spec/compilers/css_selector | 2 +- spec/compilers/css_with_arguments | 2 +- spec/compilers/html_with_pseudos | 6 +++--- spec/compilers/html_with_style | 8 ++++---- spec/compilers/html_with_style_and_custom_style | 2 +- spec/formatters/css_with_interpolation | 4 ++-- spec/parsers/css_definition_spec.cr | 12 ++++++------ spec/parsers/css_interpolation_spec.cr | 13 +++++++------ spec/style_builder_spec.cr | 4 ++-- spec/type_checking/css_definition | 10 +++++----- spec/type_checking/css_selector | 4 ++-- spec/type_checking/css_with_arguments | 4 ++-- src/formatters/css_interpolation.cr | 2 +- src/parsers/css_definition.cr | 2 +- src/parsers/css_interpolation.cr | 2 +- 17 files changed, 42 insertions(+), 41 deletions(-) diff --git a/spec/compilers/css_definition b/spec/compilers/css_definition index 4de4b44cf..e06376cbb 100644 --- a/spec/compilers/css_definition +++ b/spec/compilers/css_definition @@ -1,6 +1,6 @@ component Main { style test { - margin: {margin}px 0px; + margin: #{margin}px 0px; } get margin : Number { diff --git a/spec/compilers/css_media b/spec/compilers/css_media index 53384dce7..72f169590 100644 --- a/spec/compilers/css_media +++ b/spec/compilers/css_media @@ -1,11 +1,11 @@ component Main { style test { div { - color: {color}; + color: #{color}; } @media (screen) { - color: {color}; + color: #{color}; } } diff --git a/spec/compilers/css_selector b/spec/compilers/css_selector index d54614dee..5d76e225d 100644 --- a/spec/compilers/css_selector +++ b/spec/compilers/css_selector @@ -1,7 +1,7 @@ component Main { style test { div { - color: {color}; + color: #{color}; } &:focus { diff --git a/spec/compilers/css_with_arguments b/spec/compilers/css_with_arguments index d65037f42..ebe58955b 100644 --- a/spec/compilers/css_with_arguments +++ b/spec/compilers/css_with_arguments @@ -1,6 +1,6 @@ component Main { style test(color : String) { - color: {color}; + color: #{color}; } fun render : Html { diff --git a/spec/compilers/html_with_pseudos b/spec/compilers/html_with_pseudos index 131da6796..c5e47f2cc 100644 --- a/spec/compilers/html_with_pseudos +++ b/spec/compilers/html_with_pseudos @@ -3,16 +3,16 @@ component Main { property background : String = "blue" style test { - background: {background}; + background: #{background}; color: red; &:hover { - background: {hoverBackground}; + background: #{hoverBackground}; color: cyan; } div { - font-family: {"Hello"}; + font-family: #{"Hello"}; color: blue; } } diff --git a/spec/compilers/html_with_style b/spec/compilers/html_with_style index 5939a30db..7739ed4ec 100644 --- a/spec/compilers/html_with_style +++ b/spec/compilers/html_with_style @@ -6,10 +6,10 @@ component Main { -webkit-tap-highlight-color: rgba(0,0,0,0); -webkit-touch-callout: none; - border-color: {background}; - background: {background}; - border: {background}; - color: {color}; + border-color: #{background}; + background: #{background}; + border: #{background}; + color: #{color}; } fun render : Html { diff --git a/spec/compilers/html_with_style_and_custom_style b/spec/compilers/html_with_style_and_custom_style index d20b768ce..a79077e40 100644 --- a/spec/compilers/html_with_style_and_custom_style +++ b/spec/compilers/html_with_style_and_custom_style @@ -6,7 +6,7 @@ component Main { } style test { - background: {background}; + background: #{background}; color: red; } diff --git a/spec/formatters/css_with_interpolation b/spec/formatters/css_with_interpolation index 53b23535c..109efd601 100644 --- a/spec/formatters/css_with_interpolation +++ b/spec/formatters/css_with_interpolation @@ -1,5 +1,5 @@ component A { - styletest{background: { "red" + styletest{background: #{ "red" };color: blue;} fun render : Html { @@ -9,7 +9,7 @@ component A { -------------------------------------------------------------------------------- component A { style test { - background: {"red"}; + background: #{"red"}; color: blue; } diff --git a/spec/parsers/css_definition_spec.cr b/spec/parsers/css_definition_spec.cr index 3e7cb1314..877afb0e9 100644 --- a/spec/parsers/css_definition_spec.cr +++ b/spec/parsers/css_definition_spec.cr @@ -9,13 +9,13 @@ describe "Css Definition" do expect_error "a:", Mint::Parser::CssDefinitionExpectedSemicolon expect_error "a: b", Mint::Parser::CssDefinitionExpectedSemicolon - expect_error "a: {", Mint::Parser::CssInterpolationExpectedExpression - expect_error "a: {a", Mint::Parser::CssInterpolationExpectedClosingBracket + expect_error "a: \#{", Mint::Parser::CssInterpolationExpectedExpression + expect_error "a: \#{a", Mint::Parser::CssInterpolationExpectedClosingBracket - expect_ok "a: {a};" - expect_ok "a: x {a} v;" - expect_ok "a: x {a} v {a};" - expect_ok "a: x {a}{a};" + expect_ok "a: \#{a};" + expect_ok "a: x \#{a} v;" + expect_ok "a: x \#{a} v \#{a};" + expect_ok "a: x \#{a}\#{a};" expect_ok "a: b;" expect_ok "-WebKit-Box: 0 red;" expect_ok "-webit-box-shadow: 0 0 20px black;" diff --git a/spec/parsers/css_interpolation_spec.cr b/spec/parsers/css_interpolation_spec.cr index 9c99a3fb3..b85a998e8 100644 --- a/spec/parsers/css_interpolation_spec.cr +++ b/spec/parsers/css_interpolation_spec.cr @@ -6,12 +6,13 @@ describe "Css Interpolation" do expect_ignore "" expect_ignore "??" expect_ignore "asd" + expect_ignore "{" - expect_error "{", Mint::Parser::CssInterpolationExpectedExpression - expect_error "{ ", Mint::Parser::CssInterpolationExpectedExpression - expect_error "{a", Mint::Parser::CssInterpolationExpectedClosingBracket - expect_error "{a ", Mint::Parser::CssInterpolationExpectedClosingBracket + expect_error "\#{", Mint::Parser::CssInterpolationExpectedExpression + expect_error "\#{ ", Mint::Parser::CssInterpolationExpectedExpression + expect_error "\#{a", Mint::Parser::CssInterpolationExpectedClosingBracket + expect_error "\#{a ", Mint::Parser::CssInterpolationExpectedClosingBracket - expect_ok "{a}" - expect_ok "{ a }" + expect_ok "\#{a}" + expect_ok "\#{ a }" end diff --git a/spec/style_builder_spec.cr b/spec/style_builder_spec.cr index afce7a825..b149c74ec 100644 --- a/spec/style_builder_spec.cr +++ b/spec/style_builder_spec.cr @@ -10,7 +10,7 @@ describe Mint::StyleBuilder do span, strong { pre { - color: {"red"}; + color: \#{"red"}; } } @@ -47,7 +47,7 @@ describe Mint::StyleBuilder do @media (print) { div, p { color: black; - border-radius: {10}px; + border-radius: \#{10}px; } } } diff --git a/spec/type_checking/css_definition b/spec/type_checking/css_definition index 4afc48306..5ca9bf6d2 100644 --- a/spec/type_checking/css_definition +++ b/spec/type_checking/css_definition @@ -1,10 +1,10 @@ component Main { style test { - color: {"blue"}; - color: {color}; + color: #{"blue"}; + color: #{color}; color: red; - color: {0}; - top: {top}; + color: #{0}; + top: #{top}; } get color : String { @@ -22,7 +22,7 @@ component Main { -------------------------------------------------------CssDefinitionTypeMismatch component Main { style test { - color: {true}; + color: #{true}; } fun render : Html { diff --git a/spec/type_checking/css_selector b/spec/type_checking/css_selector index 7d772745b..5aabdc850 100644 --- a/spec/type_checking/css_selector +++ b/spec/type_checking/css_selector @@ -1,7 +1,7 @@ component Main { style test { & div { - color: {color}; + color: #{color}; } &:focus { @@ -21,7 +21,7 @@ component Main { component Main { style test { & div { - color: {color}; + color: #{color}; } &:focus { color: red; diff --git a/spec/type_checking/css_with_arguments b/spec/type_checking/css_with_arguments index e2eb93354..6cf93a4e0 100644 --- a/spec/type_checking/css_with_arguments +++ b/spec/type_checking/css_with_arguments @@ -1,6 +1,6 @@ component Main { style test(name : String) { - color: {name}; + color: #{name}; } fun render : Html { @@ -10,7 +10,7 @@ component Main { -----------------------------------------------------------------VariableMissing component Main { style test(name : String) { - color: {name2}; + color: #{name2}; } fun render : Html { diff --git a/src/formatters/css_interpolation.cr b/src/formatters/css_interpolation.cr index af7be5138..e105a962d 100644 --- a/src/formatters/css_interpolation.cr +++ b/src/formatters/css_interpolation.cr @@ -4,7 +4,7 @@ module Mint body = format node.expression - "{#{body}}" + "\#{#{body}}" end end end diff --git a/src/parsers/css_definition.cr b/src/parsers/css_definition.cr index 4e93a57e4..632bdfcdb 100644 --- a/src/parsers/css_definition.cr +++ b/src/parsers/css_definition.cr @@ -16,7 +16,7 @@ module Mint whitespace value = many(parse_whitespace: false) do - css_interpolation || gather { chars "^{;}" } + css_interpolation || gather { chars "^#;}" } end.compact char ';', CssDefinitionExpectedSemicolon diff --git a/src/parsers/css_interpolation.cr b/src/parsers/css_interpolation.cr index 6fb63b615..2f27a5150 100644 --- a/src/parsers/css_interpolation.cr +++ b/src/parsers/css_interpolation.cr @@ -5,7 +5,7 @@ module Mint def css_interpolation : Ast::CssInterpolation | Nil start do |start_position| - skip unless char! '{' + skip unless keyword "\#{" whitespace expression = expression! CssInterpolationExpectedExpression From 9ca79e86f2d0fcdb9283f3f1a24ab5d4ac62771a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guszt=C3=A1v=20Szikszai?= Date: Fri, 25 Oct 2019 15:42:59 +0200 Subject: [PATCH 18/21] Use new interpolation in tests. --- core/tests/Dom.mint | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/tests/Dom.mint b/core/tests/Dom.mint index 95f3a84c1..8e3838ba8 100644 --- a/core/tests/Dom.mint +++ b/core/tests/Dom.mint @@ -105,7 +105,7 @@ component Test.Dom.Focus { state shown : Bool = false style input { - display: {display}; + display: #{display}; } get display : String { From 3a53f35d50799f63e6cd071c1519f9c3fa448960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guszt=C3=A1v=20Szikszai?= Date: Fri, 25 Oct 2019 17:37:34 +0200 Subject: [PATCH 19/21] Try to fix CSS definition vs CSS interpolation ambiguousity. --- src/parsers/css_definition.cr | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/parsers/css_definition.cr b/src/parsers/css_definition.cr index 632bdfcdb..4fc08fef8 100644 --- a/src/parsers/css_definition.cr +++ b/src/parsers/css_definition.cr @@ -11,15 +11,17 @@ module Mint chars "a-zA-Z-" end - skip unless char!(':') + skip unless char! ':' whitespace value = many(parse_whitespace: false) do - css_interpolation || gather { chars "^#;}" } + css_interpolation || gather do + consume_while char.in_set?("^;{\0") && !keyword_ahead("\#{") + end end.compact - char ';', CssDefinitionExpectedSemicolon + skip unless char! ';' Ast::CssDefinition.new( from: start_position, From 1c0d00e574c58da659505d2a328b4cddef12bce7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guszt=C3=A1v=20Szikszai?= Date: Sat, 26 Oct 2019 08:05:23 +0200 Subject: [PATCH 20/21] Resolve CSS definition vs CSS selector ambiguity. --- src/parsers/css_definition.cr | 2 +- src/parsers/style.cr | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/parsers/css_definition.cr b/src/parsers/css_definition.cr index 4fc08fef8..da6542b91 100644 --- a/src/parsers/css_definition.cr +++ b/src/parsers/css_definition.cr @@ -21,7 +21,7 @@ module Mint end end.compact - skip unless char! ';' + char ';', CssDefinitionExpectedSemicolon Ast::CssDefinition.new( from: start_position, diff --git a/src/parsers/style.cr b/src/parsers/style.cr index 9dee9d85c..9746e2fde 100644 --- a/src/parsers/style.cr +++ b/src/parsers/style.cr @@ -47,12 +47,21 @@ module Mint def css_body many { comment || - css_definition || case_expression(for_css: true) || if_expression(for_css: true) || css_media || - css_selector + css_definition_or_selector }.compact end + + def css_definition_or_selector + css_definition || css_selector + rescue definition_errror : CssDefinitionExpectedSemicolon + begin + css_selector + rescue + raise definition_errror + end + end end end From 227a2098cc83a458b0c42e4fb56199da180c1479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guszt=C3=A1v=20Szikszai?= Date: Sat, 26 Oct 2019 15:08:23 +0200 Subject: [PATCH 21/21] Add semicolon to JS statements. --- spec/compilers/access | 10 +-- spec/compilers/access_call | 32 ++++----- spec/compilers/argument | 12 ++-- spec/compilers/array_access | 16 ++--- spec/compilers/array_literal | 12 ++-- spec/compilers/bool_literal_false | 12 ++-- spec/compilers/bool_literal_true | 12 ++-- spec/compilers/case | 16 ++--- spec/compilers/case_with_enum_destructuring | 44 ++++++------- spec/compilers/catch | 36 +++++----- spec/compilers/component | 6 +- spec/compilers/component_instance_access | 28 ++++---- spec/compilers/component_namespaced | 12 ++-- spec/compilers/component_readonly | 16 ++--- spec/compilers/component_with_provider | 18 ++--- ...nent_with_provider_and_lifecycle_functions | 24 +++---- .../component_with_provider_and_store | 36 +++++----- spec/compilers/css_definition | 14 ++-- spec/compilers/css_media | 14 ++-- spec/compilers/css_media_with_if | 14 ++-- spec/compilers/css_selector | 14 ++-- spec/compilers/css_with_arguments | 12 ++-- spec/compilers/css_with_case | 18 ++--- spec/compilers/css_with_if | 14 ++-- .../dce_remove_component_computed_property | 6 +- spec/compilers/dce_remove_component_function | 6 +- spec/compilers/dce_remove_module_function | 10 +-- spec/compilers/dce_remove_where | 10 +-- spec/compilers/dce_style | 6 +- spec/compilers/decode | 24 +++---- spec/compilers/decoder | 24 +++---- spec/compilers/encode | 14 ++-- spec/compilers/enum | 66 +++++++++---------- spec/compilers/env | 6 +- spec/compilers/finally | 24 +++---- spec/compilers/for | 14 ++-- spec/compilers/function | 12 ++-- spec/compilers/function_call_simple | 14 ++-- spec/compilers/function_call_with_arguments | 14 ++-- spec/compilers/function_with_where | 14 ++-- spec/compilers/get | 8 +-- spec/compilers/get_with_where | 10 +-- spec/compilers/html_attribute_class | 6 +- .../compilers/html_attribute_class_with_style | 8 +-- spec/compilers/html_attribute_readonly | 6 +- spec/compilers/html_attribute_ref | 14 ++-- spec/compilers/html_attribute_simple | 6 +- spec/compilers/html_attribute_with_expression | 6 +- spec/compilers/html_component | 12 ++-- spec/compilers/html_expression | 6 +- spec/compilers/html_fragment | 6 +- spec/compilers/html_fragment_empty | 6 +- spec/compilers/html_with_custom_style | 12 ++-- spec/compilers/html_with_multiple_styles | 8 +-- spec/compilers/html_with_pseudos | 16 ++--- spec/compilers/html_with_string_style | 6 +- spec/compilers/html_with_style | 16 ++--- .../html_with_style_and_custom_style | 18 ++--- spec/compilers/if | 12 ++-- spec/compilers/indirect_connect | 30 ++++----- spec/compilers/inline_function | 8 +-- spec/compilers/inline_function_with_arguments | 14 ++-- spec/compilers/js | 6 +- spec/compilers/js_with_double_interpolation | 8 +-- spec/compilers/js_with_interpolation | 8 +-- spec/compilers/member_access | 16 ++--- spec/compilers/module | 10 +-- spec/compilers/module_access | 14 ++-- spec/compilers/module_access_get | 16 ++--- spec/compilers/module_access_subscriptions | 22 +++---- spec/compilers/module_call | 12 ++-- spec/compilers/module_call_piped | 12 ++-- spec/compilers/next_call | 20 +++--- spec/compilers/number_literal_negative | 12 ++-- spec/compilers/number_literal_simple | 12 ++-- spec/compilers/number_literal_with_decimal | 12 ++-- spec/compilers/operation_chanined | 12 ++-- spec/compilers/operation_simple | 12 ++-- spec/compilers/parallel_simple | 30 ++++----- spec/compilers/parallel_with_catch | 54 +++++++-------- spec/compilers/parallel_with_catch_all | 32 ++++----- spec/compilers/parenthesized_expression | 12 ++-- spec/compilers/partial_application | 12 ++-- spec/compilers/property | 10 +-- spec/compilers/record | 14 ++-- spec/compilers/record_field | 14 ++-- spec/compilers/record_update | 18 ++--- spec/compilers/route | 2 +- spec/compilers/sequence_simple | 24 +++---- spec/compilers/sequence_using_argument | 32 ++++----- spec/compilers/sequence_with_argument | 26 ++++---- spec/compilers/sequence_with_catch | 52 +++++++-------- spec/compilers/sequence_with_finally | 24 +++---- spec/compilers/sequence_with_result_and_catch | 42 ++++++------ .../sequence_with_result_and_catch_all | 30 ++++----- spec/compilers/state | 16 ++--- spec/compilers/statement | 10 +-- spec/compilers/store | 22 +++---- spec/compilers/store_with_get | 22 +++---- spec/compilers/string_literal_escaped | 6 +- spec/compilers/string_literal_simple | 6 +- spec/compilers/string_literal_with_backtick | 6 +- spec/compilers/try | 8 +-- spec/compilers/try_with_catch_all | 22 +++---- spec/compilers/try_with_catches | 24 +++---- spec/compilers/try_with_statement | 10 +-- spec/compilers/variable_argument | 8 +-- spec/compilers/variable_component_function | 8 +-- spec/compilers/variable_component_get | 8 +-- spec/compilers/variable_component_property | 10 +-- spec/compilers/variable_module_function | 14 ++-- spec/compilers/variable_where | 10 +-- spec/compilers/void | 12 ++-- spec/compilers/where | 10 +-- spec/compilers/with | 10 +-- src/compilers/component.cr | 6 +- src/compilers/property.cr | 2 +- src/compilers/state.cr | 2 +- src/js.cr | 1 + 119 files changed, 913 insertions(+), 912 deletions(-) diff --git a/spec/compilers/access b/spec/compilers/access index 731825c34..c08aee46e 100644 --- a/spec/compilers/access +++ b/spec/compilers/access @@ -15,16 +15,16 @@ const A = _R({ "name", Decoder.string ] -}) +}); class B extends _C { render() { let a = new A({ name: `test` - }) + }); - return a.name + return a.name; } -} +}; -B.displayName = "Main" +B.displayName = "Main"; diff --git a/spec/compilers/access_call b/spec/compilers/access_call index 8679b24be..291b85083 100644 --- a/spec/compilers/access_call +++ b/spec/compilers/access_call @@ -26,34 +26,34 @@ component Main { -------------------------------------------------------------------------------- class C extends _E { constructor(_0) { - super() - this._0 = _0 - this.length = 1 + super(); + this._0 = _0; + this.length = 1; } -} +}; class A extends _C { a(b) { - return b + return b; } render() { - return _h("div", {}) + return _h("div", {}); } -} +}; -A.displayName = "Test" +A.displayName = "Test"; class B extends _C { c() { return (() => { const _ = (() => { - const _ = this._test - return _s(_,(_) => _.a) - })() + const _ = this._test; + return _s(_,(_) => _.a); + })(); - return _s(_,(_) => _(`asd`)) - })() + return _s(_,(_) => _(`asd`)); + })(); } render() { @@ -63,8 +63,8 @@ class B extends _C { _h(A, { ref: (instance) => { this._test = new C(instance) } }) - ]) + ]); } -} +}; -B.displayName = "Main" +B.displayName = "Main"; diff --git a/spec/compilers/argument b/spec/compilers/argument index 9bd4218ba..949f1df38 100644 --- a/spec/compilers/argument +++ b/spec/compilers/argument @@ -14,15 +14,15 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { a(c, b) { - return b + return b; } render() { return (() => { - this.a(``, 0) - return `` - })() + this.a(``, 0); + return ``; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/array_access b/spec/compilers/array_access index 07e999905..5c426d600 100644 --- a/spec/compilers/array_access +++ b/spec/compilers/array_access @@ -24,20 +24,20 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { a() { - return _at([`Hello`, `Blah`, `Joe`], 1) + return _at([`Hello`, `Blah`, `Joe`], 1); } b() { - return _at([], 1) + return _at([], 1); } render() { return (() => { - this.a() - this.b() - return `` - })() + this.a(); + this.b(); + return ``; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/array_literal b/spec/compilers/array_literal index 1565ad2ad..a59ad55ae 100644 --- a/spec/compilers/array_literal +++ b/spec/compilers/array_literal @@ -18,15 +18,15 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { a() { - return [`Hello`, `Blah`, `Joe`] + return [`Hello`, `Blah`, `Joe`]; } render() { return (() => { - this.a() - return `` - })() + this.a(); + return ``; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/bool_literal_false b/spec/compilers/bool_literal_false index f37c1a883..41514d240 100644 --- a/spec/compilers/bool_literal_false +++ b/spec/compilers/bool_literal_false @@ -14,15 +14,15 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { a() { - return false + return false; } render() { return (() => { - this.a() - return `` - })() + this.a(); + return ``; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/bool_literal_true b/spec/compilers/bool_literal_true index 291bf420c..2d768d8e5 100644 --- a/spec/compilers/bool_literal_true +++ b/spec/compilers/bool_literal_true @@ -14,15 +14,15 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { a() { - return true + return true; } render() { return (() => { - this.a() - return `` - })() + this.a(); + return ``; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/case b/spec/compilers/case index c6e6d5bc7..5cb2788cb 100644 --- a/spec/compilers/case +++ b/spec/compilers/case @@ -19,7 +19,7 @@ component Main { class A extends _C { a() { return (() => { - let b = `Hello` + let b = `Hello`; if (_compare(b, `test`)) { return true @@ -27,16 +27,16 @@ class A extends _C { return false } else { return false - } - })() + }; + })(); } render() { return (() => { - this.a() - return `` - })() + this.a(); + return ``; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/case_with_enum_destructuring b/spec/compilers/case_with_enum_destructuring index a80c09e1e..654efaf89 100644 --- a/spec/compilers/case_with_enum_destructuring +++ b/spec/compilers/case_with_enum_destructuring @@ -28,46 +28,46 @@ component Main { -------------------------------------------------------------------------------- class B extends _E { constructor(_0) { - super() - this._0 = _0 - this.length = 1 + super(); + this._0 = _0; + this.length = 1; } -} +}; class C extends _E { constructor(_0) { - super() - this._0 = _0 - this.length = 1 + super(); + this._0 = _0; + this.length = 1; } -} +}; class A extends _C { a(b) { return (() => { - let c = b + let c = b; if (c instanceof C) { - const d = c._0 + const d = c._0; return (() => { - let e = d + let e = d; if (e instanceof B) { - const f = e._0 - return f - } - })() - } - })() + const f = e._0; + return f; + }; + })(); + }; + })(); } render() { return (() => { - this.a(new C(new B(``))) - return `` - })() + this.a(new C(new B(``))); + return ``; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/catch b/spec/compilers/catch index 8d4165285..0abb51321 100644 --- a/spec/compilers/catch +++ b/spec/compilers/catch @@ -27,44 +27,44 @@ component Main { -------------------------------------------------------------------------------- const B = new(class extends _M { c(e) { - return + return; } -}) +}); class A extends _C { a() { return (async () => { - let _ = null + let _ = null; try { let b = await (async () => { try { return await B.c(`hello`) } catch (_error) { - let d = _error - _ = null - throw new DoError() + let d = _error; + _ = null; + throw new DoError(); } - })() + })(); - _ = await null + _ = await null; } catch (_error) { if (!(_error instanceof DoError)) { - console.warn(`Unhandled error in sequence expression:`) - console.warn(_error) + console.warn(`Unhandled error in sequence expression:`); + console.warn(_error); } - } + }; - return _ - })() + return _; + })(); } render() { return (() => { - this.a() - return `` - })() + this.a(); + return ``; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/component b/spec/compilers/component index 56e13ba11..89352d8f3 100644 --- a/spec/compilers/component +++ b/spec/compilers/component @@ -6,8 +6,8 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { render() { - return _h("div", {}) + return _h("div", {}); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/component_instance_access b/spec/compilers/component_instance_access index 96b888f2f..059b22184 100644 --- a/spec/compilers/component_instance_access +++ b/spec/compilers/component_instance_access @@ -26,30 +26,30 @@ component Main { -------------------------------------------------------------------------------- class C extends _E { constructor(_0) { - super() - this._0 = _0 - this.length = 1 + super(); + this._0 = _0; + this.length = 1; } -} +}; class A extends _C { get a() { - return `Instance` + return `Instance`; } render() { - return _h("div", {}) + return _h("div", {}); } -} +}; -A.displayName = "Instance" +A.displayName = "Instance"; class B extends _C { b() { return (() => { - const _ = this._instance - return _s(_,(_) => _.a) - })() + const _ = this._instance; + return _s(_,(_) => _.a); + })(); } render() { @@ -59,8 +59,8 @@ class B extends _C { _h(A, { ref: (instance) => { this._instance = new C(instance) } }) - ]) + ]); } -} +}; -B.displayName = "Main" +B.displayName = "Main"; diff --git a/spec/compilers/component_namespaced b/spec/compilers/component_namespaced index 277e12d53..115fbbb05 100644 --- a/spec/compilers/component_namespaced +++ b/spec/compilers/component_namespaced @@ -12,16 +12,16 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { render() { - return `test` + return `test`; } -} +}; -A.displayName = "Ui.Dropdown" +A.displayName = "Ui.Dropdown"; class B extends _C { render() { - return _h(A, {}) + return _h(A, {}); } -} +}; -B.displayName = "Main" +B.displayName = "Main"; diff --git a/spec/compilers/component_readonly b/spec/compilers/component_readonly index 283fdc07a..3a97eb50c 100644 --- a/spec/compilers/component_readonly +++ b/spec/compilers/component_readonly @@ -14,29 +14,29 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { constructor(props) { - super(props) + super(props); this._d({ a: [ null, false ] - }) + }); } render() { - return _h("div", {}) + return _h("div", {}); } -} +}; -A.displayName = "Test" +A.displayName = "Test"; class B extends _C { render() { return _h(A, { a: true - }) + }); } -} +}; -B.displayName = "Main" +B.displayName = "Main"; diff --git a/spec/compilers/component_with_provider b/spec/compilers/component_with_provider index 15c817916..bbf261a00 100644 --- a/spec/compilers/component_with_provider +++ b/spec/compilers/component_with_provider @@ -22,17 +22,17 @@ component Main { } } -------------------------------------------------------------------------------- -const A = _R({}) +const A = _R({}); const B = new(class extends _P { attach() { - return null + return null; } -}) +}); class C extends _C { componentWillUnmount() { - B._unsubscribe(this) + B._unsubscribe(this); } componentDidUpdate() { @@ -47,7 +47,7 @@ class C extends _C { })) } else { B._unsubscribe(this) - } + }; } componentDidMount() { @@ -62,12 +62,12 @@ class C extends _C { })) } else { B._unsubscribe(this) - } + }; } render() { - return _h("div", {}) + return _h("div", {}); } -} +}; -C.displayName = "Main" +C.displayName = "Main"; diff --git a/spec/compilers/component_with_provider_and_lifecycle_functions b/spec/compilers/component_with_provider_and_lifecycle_functions index 3f8512eb0..8f10ee8e9 100644 --- a/spec/compilers/component_with_provider_and_lifecycle_functions +++ b/spec/compilers/component_with_provider_and_lifecycle_functions @@ -34,18 +34,18 @@ component Main { } } -------------------------------------------------------------------------------- -const A = _R({}) +const A = _R({}); const B = new(class extends _P { attach() { - return null + return null; } -}) +}); class C extends _C { componentWillUnmount() { - B._unsubscribe(this) - return null + B._unsubscribe(this); + return null; } componentDidUpdate() { @@ -60,9 +60,9 @@ class C extends _C { })) } else { B._unsubscribe(this) - } + }; - return null + return null; } componentDidMount() { @@ -77,14 +77,14 @@ class C extends _C { })) } else { B._unsubscribe(this) - } + }; - return null + return null; } render() { - return _h("div", {}) + return _h("div", {}); } -} +}; -C.displayName = "Main" +C.displayName = "Main"; diff --git a/spec/compilers/component_with_provider_and_store b/spec/compilers/component_with_provider_and_store index 413c08e8d..1f0256000 100644 --- a/spec/compilers/component_with_provider_and_store +++ b/spec/compilers/component_with_provider_and_store @@ -32,24 +32,24 @@ component Main { } } -------------------------------------------------------------------------------- -const A = _R({}) +const A = _R({}); const B = new(class extends _P { attach() { - return null + return null; } -}) +}); class C extends _C { get d() { - return D.c + return D.c; } - f (...params) { return D.e(...params) } + f (...params) { return D.e(...params); } componentWillUnmount() { - D._unsubscribe(this) - B._unsubscribe(this) + D._unsubscribe(this); + B._unsubscribe(this); } componentDidUpdate() { @@ -64,11 +64,11 @@ class C extends _C { })) } else { B._unsubscribe(this) - } + }; } componentDidMount() { - D._subscribe(this) + D._subscribe(this); if (false) { B._subscribe(this, new A({ @@ -81,30 +81,30 @@ class C extends _C { })) } else { B._unsubscribe(this) - } + }; } render() { - return _h("div", {}) + return _h("div", {}); } -} +}; -C.displayName = "Main" +C.displayName = "Main"; const D = new(class extends _S { constructor() { - super() + super(); this.state = { c: `` - } + }; } get c() { - return this.state.c + return this.state.c; } e() { - return `hello` + return `hello`; } -}) +}); diff --git a/spec/compilers/css_definition b/spec/compilers/css_definition index e06376cbb..4a9925a08 100644 --- a/spec/compilers/css_definition +++ b/spec/compilers/css_definition @@ -16,27 +16,27 @@ class A extends _C { $a() { const _ = { [`--a-a`]: this.a + `px 0px` - } + }; - return _ + return _; } get a() { - return 10 + return 10; } render() { return _h("div", { className: `a`, style: _style([this.$a()]) - }) + }); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; _insertStyles(` .a { margin: var(--a-a); } -`) +`); diff --git a/spec/compilers/css_media b/spec/compilers/css_media index 72f169590..2ff155219 100644 --- a/spec/compilers/css_media +++ b/spec/compilers/css_media @@ -23,24 +23,24 @@ class A extends _C { const _ = { [`--a-a`]: this.a, [`--b-a`]: this.a - } + }; - return _ + return _; } get a() { - return `blue` + return `blue`; } render() { return _h("div", { className: `a`, style: _style([this.$a()]) - }) + }); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; _insertStyles(` .a div { @@ -52,4 +52,4 @@ _insertStyles(` color: var(--b-a); } } -`) +`); diff --git a/spec/compilers/css_media_with_if b/spec/compilers/css_media_with_if index 43645833b..e3a94053a 100644 --- a/spec/compilers/css_media_with_if +++ b/spec/compilers/css_media_with_if @@ -16,24 +16,24 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { $a() { - const _ = {} + const _ = {}; (true ? Object.assign(_, { [`--a-a`]: `red` - }) : null) + }) : null); - return _ + return _; } render() { return _h("div", { className: `a`, style: _style([this.$a()]) - }) + }); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; _insertStyles(` .a { @@ -45,4 +45,4 @@ _insertStyles(` color: var(--a-a); } } -`) +`); diff --git a/spec/compilers/css_selector b/spec/compilers/css_selector index 5d76e225d..5302ec8a5 100644 --- a/spec/compilers/css_selector +++ b/spec/compilers/css_selector @@ -22,24 +22,24 @@ class A extends _C { $a() { const _ = { [`--a-a`]: this.a - } + }; - return _ + return _; } get a() { - return `blue` + return `blue`; } render() { return _h("div", { className: `a`, style: _style([this.$a()]) - }) + }); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; _insertStyles(` .a div { @@ -49,4 +49,4 @@ _insertStyles(` .a:focus { color: red; } -`) +`); diff --git a/spec/compilers/css_with_arguments b/spec/compilers/css_with_arguments index ebe58955b..39d38fcb4 100644 --- a/spec/compilers/css_with_arguments +++ b/spec/compilers/css_with_arguments @@ -12,23 +12,23 @@ class A extends _C { $a(a) { const _ = { [`--a-a`]: a - } + }; - return _ + return _; } render() { return _h("div", { className: `a`, style: _style([this.$a(`red`)]) - }) + }); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; _insertStyles(` .a { color: var(--a-a); } -`) +`); diff --git a/spec/compilers/css_with_case b/spec/compilers/css_with_case index a2c95d459..fa9eb02c4 100644 --- a/spec/compilers/css_with_case +++ b/spec/compilers/css_with_case @@ -18,10 +18,10 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { $a() { - const _ = {} + const _ = {}; (() => { - let a = `a` + let a = `a`; if (_compare(a, `a`)) { Object.assign(_, { @@ -31,24 +31,24 @@ class A extends _C { Object.assign(_, { [`--a-a`]: `blue` }) - } - })() + }; + })(); - return _ + return _; } render() { return _h("div", { className: `a`, style: _style([this.$a()]) - }) + }); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; _insertStyles(` .a { color: var(--a-a, yellow); } -`) +`); diff --git a/spec/compilers/css_with_if b/spec/compilers/css_with_if index 701aad866..8b16632da 100644 --- a/spec/compilers/css_with_if +++ b/spec/compilers/css_with_if @@ -16,29 +16,29 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { $a() { - const _ = {} + const _ = {}; (true ? Object.assign(_, { [`--a-a`]: `red` }) : Object.assign(_, { [`--a-a`]: `blue` - })) + })); - return _ + return _; } render() { return _h("div", { className: `a`, style: _style([this.$a()]) - }) + }); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; _insertStyles(` .a { color: var(--a-a, yellow); } -`) +`); diff --git a/spec/compilers/dce_remove_component_computed_property b/spec/compilers/dce_remove_component_computed_property index cac50beb6..70322d3f2 100644 --- a/spec/compilers/dce_remove_component_computed_property +++ b/spec/compilers/dce_remove_component_computed_property @@ -10,8 +10,8 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { render() { - return _h("div", {}) + return _h("div", {}); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/dce_remove_component_function b/spec/compilers/dce_remove_component_function index e6e130176..28cd03654 100644 --- a/spec/compilers/dce_remove_component_function +++ b/spec/compilers/dce_remove_component_function @@ -10,8 +10,8 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { render() { - return _h("div", {}) + return _h("div", {}); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/dce_remove_module_function b/spec/compilers/dce_remove_module_function index 67c2335cc..4e782b251 100644 --- a/spec/compilers/dce_remove_module_function +++ b/spec/compilers/dce_remove_module_function @@ -16,14 +16,14 @@ component Main { -------------------------------------------------------------------------------- const B = new(class extends _M { a() { - return `` + return ``; } -}) +}); class A extends _C { render() { - return B.a() + return B.a(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/dce_remove_where b/spec/compilers/dce_remove_where index a5998a2e4..fc6f917c3 100644 --- a/spec/compilers/dce_remove_where +++ b/spec/compilers/dce_remove_where @@ -16,13 +16,13 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { get a() { - let b = `Hello` - return b + let b = `Hello`; + return b; } render() { - return this.a + return this.a; } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/dce_style b/spec/compilers/dce_style index bed97835f..4ba592246 100644 --- a/spec/compilers/dce_style +++ b/spec/compilers/dce_style @@ -10,8 +10,8 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { render() { - return _h("div", {}) + return _h("div", {}); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/decode b/spec/compilers/decode index 93e2e857a..3a8dcf5f5 100644 --- a/spec/compilers/decode +++ b/spec/compilers/decode @@ -26,7 +26,7 @@ const A = _R({ "blah", Decoder.string ] -}) +}); const B = _R({ name: [ @@ -37,27 +37,27 @@ const B = _R({ "y", ((_)=>A.decode(_)) ] -}) +}); class C extends _C { a(b) { - return ((_)=>B.decode(_))(b) + return ((_)=>B.decode(_))(b); } render() { return (() => { - let _0 = this.a() + let _0 = this.a(); if (_0 instanceof Err) { - let _error = _0._0 - return _catch_all() - } + let _error = _0._0; + return _catch_all(); + }; - _0._0 + _0._0; - return `` - })() + return ``; + })(); } -} +}; -C.displayName = "Main" +C.displayName = "Main"; diff --git a/spec/compilers/decoder b/spec/compilers/decoder index f8bd548e6..c7fee9ec1 100644 --- a/spec/compilers/decoder +++ b/spec/compilers/decoder @@ -31,7 +31,7 @@ const A = _R({ "SIIIZEEE", Decoder.number ] -}) +}); const B = _R({ string: [ @@ -62,27 +62,27 @@ const B = _R({ "y", ((_)=>A.decode(_)) ] -}) +}); class C extends _C { a(b) { - return ((_)=>B.decode(_))(b) + return ((_)=>B.decode(_))(b); } render() { return (() => { - let _0 = this.a() + let _0 = this.a(); if (_0 instanceof Err) { - let _error = _0._0 - return _catch_all() - } + let _error = _0._0; + return _catch_all(); + }; - _0._0 + _0._0; - return `` - })() + return ``; + })(); } -} +}; -C.displayName = "Main" +C.displayName = "Main"; diff --git a/spec/compilers/encode b/spec/compilers/encode index a76cb0105..eb62f8870 100644 --- a/spec/compilers/encode +++ b/spec/compilers/encode @@ -26,22 +26,22 @@ const A = _R({ "age", Decoder.number ] -}) +}); class B extends _C { a() { return _encode(new A({ name: `Hello`, age: 20 - })) + })); } render() { return (() => { - this.a() - return `` - })() + this.a(); + return ``; + })(); } -} +}; -B.displayName = "Main" +B.displayName = "Main"; diff --git a/spec/compilers/enum b/spec/compilers/enum index 72ddf8ff7..951d4bbcb 100644 --- a/spec/compilers/enum +++ b/spec/compilers/enum @@ -36,72 +36,72 @@ component Main { -------------------------------------------------------------------------------- class B extends _E { constructor() { - super() - this.length = 0 + super(); + this.length = 0; } -} +}; class F extends _E { constructor() { - super() - this.length = 0 + super(); + this.length = 0; } -} +}; class D extends _E { constructor(_0) { - super() - this._0 = _0 - this.length = 1 + super(); + this._0 = _0; + this.length = 1; } -} +}; class E extends _E { constructor(_0) { - super() - this._0 = _0 - this.length = 1 + super(); + this._0 = _0; + this.length = 1; } -} +}; class G extends _E { constructor(_0) { - super() - this._0 = _0 - this.length = 1 + super(); + this._0 = _0; + this.length = 1; } -} +}; class C extends _E { constructor(_0, _1) { - super() - this._0 = _0 - this._1 = _1 - this.length = 2 + super(); + this._0 = _0; + this._1 = _1; + this.length = 2; } -} +}; class A extends _C { a() { - return new B() + return new B(); } b() { - return new C(``,``) + return new C(``,``); } c() { - return new D(new E(``)) + return new D(new E(``)); } render() { return (() => { - this.a() - this.b() - this.c() - return `` - })() + this.a(); + this.b(); + this.c(); + return ``; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/env b/spec/compilers/env index e3b649ff9..0c593f0f0 100644 --- a/spec/compilers/env +++ b/spec/compilers/env @@ -6,8 +6,8 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { render() { - return `YES` + return `YES`; } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/finally b/spec/compilers/finally index 64414f38e..b272f8cd2 100644 --- a/spec/compilers/finally +++ b/spec/compilers/finally @@ -19,29 +19,29 @@ component Main { class A extends _C { a() { return (async () => { - let _ = null + let _ = null; try { - _ = await null + _ = await null; } catch (_error) { if (!(_error instanceof DoError)) { - console.warn(`Unhandled error in sequence expression:`) - console.warn(_error) + console.warn(`Unhandled error in sequence expression:`); + console.warn(_error); } } finally { null - } + }; - return _ - })() + return _; + })(); } render() { return (() => { - this.a() - return `` - })() + this.a(); + return ``; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/for b/spec/compilers/for index 0173cc4fa..37e3c214f 100644 --- a/spec/compilers/for +++ b/spec/compilers/for @@ -11,18 +11,18 @@ component Main { class A extends _C { render() { return (() => { - const _0 = [] - const _1 = [`A`, `B`] + const _0 = []; + const _1 = [`A`, `B`]; for (let a of _1) { _0.push(_h("div", {}, [ a ])) - } + }; - return _0 - })() + return _0; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/function b/spec/compilers/function index 38f58f959..3eed54510 100644 --- a/spec/compilers/function +++ b/spec/compilers/function @@ -14,16 +14,16 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { a() { - return true + return true; } render() { return (() => { - this.a() - return `` - })() + this.a(); + return ``; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/function_call_simple b/spec/compilers/function_call_simple index 15c2be949..5fe94d8ad 100644 --- a/spec/compilers/function_call_simple +++ b/spec/compilers/function_call_simple @@ -18,19 +18,19 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { a() { - return `test` + return `test`; } b() { - return this.a() + return this.a(); } render() { return (() => { - this.b() - return `` - })() + this.b(); + return ``; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/function_call_with_arguments b/spec/compilers/function_call_with_arguments index 0f8664f97..b79c08886 100644 --- a/spec/compilers/function_call_with_arguments +++ b/spec/compilers/function_call_with_arguments @@ -18,20 +18,20 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { a(c, b) { - return b + return b; } d() { - return this.a(`Hello`, true) + return this.a(`Hello`, true); } render() { return (() => { - this.d() - return `` - })() + this.d(); + return ``; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/function_with_where b/spec/compilers/function_with_where index d37048c7e..5b1f24cfd 100644 --- a/spec/compilers/function_with_where +++ b/spec/compilers/function_with_where @@ -17,17 +17,17 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { a() { - let b = `Asd` - return b + let b = `Asd`; + return b; } render() { return (() => { - this.a() - return `` - })() + this.a(); + return ``; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/get b/spec/compilers/get index c63a47da0..cdaf514da 100644 --- a/spec/compilers/get +++ b/spec/compilers/get @@ -10,12 +10,12 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { get a() { - return `` + return ``; } render() { - return this.a + return this.a; } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/get_with_where b/spec/compilers/get_with_where index ebb067b9e..05316ea4e 100644 --- a/spec/compilers/get_with_where +++ b/spec/compilers/get_with_where @@ -16,13 +16,13 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { get a() { - let b = `Asd` - return b + let b = `Asd`; + return b; } render() { - return this.a + return this.a; } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/html_attribute_class b/spec/compilers/html_attribute_class index b6f9886db..17259cdf8 100644 --- a/spec/compilers/html_attribute_class +++ b/spec/compilers/html_attribute_class @@ -9,8 +9,8 @@ class A extends _C { render() { return _h("div", { className: `something` - }) + }); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/html_attribute_class_with_style b/spec/compilers/html_attribute_class_with_style index fc43e0662..b826f2e05 100644 --- a/spec/compilers/html_attribute_class_with_style +++ b/spec/compilers/html_attribute_class_with_style @@ -13,14 +13,14 @@ class A extends _C { render() { return _h("div", { className: `something` + ` a` - }) + }); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; _insertStyles(` .a { width: 100%; } -`) +`); diff --git a/spec/compilers/html_attribute_readonly b/spec/compilers/html_attribute_readonly index e130578d9..e091dd70f 100644 --- a/spec/compilers/html_attribute_readonly +++ b/spec/compilers/html_attribute_readonly @@ -9,8 +9,8 @@ class A extends _C { render() { return _h("div", { readOnly: true - }) + }); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/html_attribute_ref b/spec/compilers/html_attribute_ref index 1b71e9d31..7d2209e4a 100644 --- a/spec/compilers/html_attribute_ref +++ b/spec/compilers/html_attribute_ref @@ -11,18 +11,18 @@ component Main { -------------------------------------------------------------------------------- class B extends _E { constructor(_0) { - super() - this._0 = _0 - this.length = 1 + super(); + this._0 = _0; + this.length = 1; } -} +}; class A extends _C { render() { return _h("div", { ref: (element) => { this._input = new B(element) } - }) + }); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/html_attribute_simple b/spec/compilers/html_attribute_simple index b16bcd3e4..26f17c858 100644 --- a/spec/compilers/html_attribute_simple +++ b/spec/compilers/html_attribute_simple @@ -9,8 +9,8 @@ class A extends _C { render() { return _h("div", { "title": `Hello` - }) + }); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/html_attribute_with_expression b/spec/compilers/html_attribute_with_expression index cb8b2c85e..419923b57 100644 --- a/spec/compilers/html_attribute_with_expression +++ b/spec/compilers/html_attribute_with_expression @@ -9,8 +9,8 @@ class A extends _C { render() { return _h("div", { "title": `Hello ` + `there!` - }) + }); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/html_component b/spec/compilers/html_component index 7403019db..4df1e5738 100644 --- a/spec/compilers/html_component +++ b/spec/compilers/html_component @@ -12,16 +12,16 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { render() { - return _h("div", {}) + return _h("div", {}); } -} +}; -A.displayName = "Test" +A.displayName = "Test"; class B extends _C { render() { - return _h(A, {}) + return _h(A, {}); } -} +}; -B.displayName = "Main" +B.displayName = "Main"; diff --git a/spec/compilers/html_expression b/spec/compilers/html_expression index 6ee5f206d..659fd2859 100644 --- a/spec/compilers/html_expression +++ b/spec/compilers/html_expression @@ -8,8 +8,8 @@ class A extends _C { render() { return _h("div", {}, [ `Hello` - ]) + ]); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/html_fragment b/spec/compilers/html_fragment index daa9bf130..34b76bb81 100644 --- a/spec/compilers/html_fragment +++ b/spec/compilers/html_fragment @@ -18,8 +18,8 @@ class A extends _C { key: `something` }, []) ]) - ]) + ]); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/html_fragment_empty b/spec/compilers/html_fragment_empty index d560560ae..7a2712a77 100644 --- a/spec/compilers/html_fragment_empty +++ b/spec/compilers/html_fragment_empty @@ -11,8 +11,8 @@ class A extends _C { render() { return _h("div", {}, [ null - ]) + ]); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/html_with_custom_style b/spec/compilers/html_with_custom_style index 595602abc..047cd4b9e 100644 --- a/spec/compilers/html_with_custom_style +++ b/spec/compilers/html_with_custom_style @@ -13,25 +13,25 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { constructor(props) { - super(props) + super(props); this._d({ b: [ null, `blue` ] - }) + }); } get a() { - return + return; } render() { return _h("div", { style: _style([this.a]) - }) + }); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/html_with_multiple_styles b/spec/compilers/html_with_multiple_styles index 97630fef1..7eaaad503 100644 --- a/spec/compilers/html_with_multiple_styles +++ b/spec/compilers/html_with_multiple_styles @@ -17,11 +17,11 @@ class A extends _C { render() { return _h("div", { className: `a b` - }) + }); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; _insertStyles(` .a { @@ -31,4 +31,4 @@ _insertStyles(` .b { color: blue; } -`) +`); diff --git a/spec/compilers/html_with_pseudos b/spec/compilers/html_with_pseudos index c5e47f2cc..2c486a9cc 100644 --- a/spec/compilers/html_with_pseudos +++ b/spec/compilers/html_with_pseudos @@ -25,7 +25,7 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { constructor(props) { - super(props) + super(props); this._d({ b: [ @@ -36,7 +36,7 @@ class A extends _C { null, `blue` ] - }) + }); } $a() { @@ -44,20 +44,20 @@ class A extends _C { [`--a-a`]: this.a, [`--b-a`]: this.b, [`--c-a`]: `Hello` - } + }; - return _ + return _; } render() { return _h("div", { className: `a`, style: _style([this.$a()]) - }) + }); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; _insertStyles(` .a { @@ -74,4 +74,4 @@ _insertStyles(` font-family: var(--c-a); color: blue; } -`) +`); diff --git a/spec/compilers/html_with_string_style b/spec/compilers/html_with_string_style index 92b62385d..c0f17fdab 100644 --- a/spec/compilers/html_with_string_style +++ b/spec/compilers/html_with_string_style @@ -8,8 +8,8 @@ class A extends _C { render() { return _h("div", { style: _style([`opacity:0;`]) - }) + }); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/html_with_style b/spec/compilers/html_with_style index 7739ed4ec..6ddb4069e 100644 --- a/spec/compilers/html_with_style +++ b/spec/compilers/html_with_style @@ -20,7 +20,7 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { constructor(props) { - super(props) + super(props); this._d({ a: [ @@ -31,7 +31,7 @@ class A extends _C { null, `yellow` ] - }) + }); } $a() { @@ -40,20 +40,20 @@ class A extends _C { [`--a-b`]: this.a, [`--a-c`]: this.a, [`--a-d`]: this.b - } + }; - return _ + return _; } render() { return _h("div", { className: `a`, style: _style([this.$a()]) - }) + }); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; _insertStyles(` .a { @@ -64,4 +64,4 @@ _insertStyles(` border: var(--a-c); color: var(--a-d); } -`) +`); diff --git a/spec/compilers/html_with_style_and_custom_style b/spec/compilers/html_with_style_and_custom_style index a79077e40..7e5352949 100644 --- a/spec/compilers/html_with_style_and_custom_style +++ b/spec/compilers/html_with_style_and_custom_style @@ -18,41 +18,41 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { constructor(props) { - super(props) + super(props); this._d({ a: [ null, `blue` ] - }) + }); } $a() { const _ = { [`--a-a`]: this.a - } + }; - return _ + return _; } get b() { - return + return; } render() { return _h("div", { className: `a`, style: _style([this.$a(), this.b]) - }) + }); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; _insertStyles(` .a { background: var(--a-a); color: red; } -`) +`); diff --git a/spec/compilers/if b/spec/compilers/if index 085a2ef17..c76cee97e 100644 --- a/spec/compilers/if +++ b/spec/compilers/if @@ -18,15 +18,15 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { a() { - return (_compare(`asd`, `asd2`) ? true : false) + return (_compare(`asd`, `asd2`) ? true : false); } render() { return (() => { - this.a() - return `` - })() + this.a(); + return ``; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/indirect_connect b/spec/compilers/indirect_connect index f804d3042..a24baf41c 100644 --- a/spec/compilers/indirect_connect +++ b/spec/compilers/indirect_connect @@ -21,52 +21,52 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { get c() { - return B.b + return B.b; } componentWillUnmount() { - B._unsubscribe(this) + B._unsubscribe(this); } componentDidMount() { - B._subscribe(this) + B._subscribe(this); } render() { - return C.a + return C.a; } -} +}; -A.displayName = "Main" +A.displayName = "Main"; const B = new(class extends _S { constructor() { - super() + super(); this.state = { b: `` - } + }; } get b() { - return this.state.b + return this.state.b; } d() { - return `hello` + return `hello`; } -}) +}); const C = new(class extends _S { constructor() { - super() + super(); this.state = { a: `` - } + }; } get a() { - return this.state.a + return this.state.a; } -}) +}); diff --git a/spec/compilers/inline_function b/spec/compilers/inline_function index f5bdaed55..86d4d9487 100644 --- a/spec/compilers/inline_function +++ b/spec/compilers/inline_function @@ -10,10 +10,10 @@ class A extends _C { render() { let a = () => { return `Hello` - } + }; - return a() + return a(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/inline_function_with_arguments b/spec/compilers/inline_function_with_arguments index be247eb6a..5fe03a8bf 100644 --- a/spec/compilers/inline_function_with_arguments +++ b/spec/compilers/inline_function_with_arguments @@ -19,17 +19,17 @@ class A extends _C { a() { let b = (c) => { return c - } + }; - return b(`Joe`) + return b(`Joe`); } render() { return (() => { - this.a() - return `` - })() + this.a(); + return ``; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/js b/spec/compilers/js index 522972383..2dbf3760a 100644 --- a/spec/compilers/js +++ b/spec/compilers/js @@ -6,8 +6,8 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { render() { - return ("Hello") + return ("Hello"); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/js_with_double_interpolation b/spec/compilers/js_with_double_interpolation index a84ba5b89..1bbbe314d 100644 --- a/spec/compilers/js_with_double_interpolation +++ b/spec/compilers/js_with_double_interpolation @@ -10,12 +10,12 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { a(b) { - return b + return b; } render() { - return ("Hello" + this.a(("World!"))) + return ("Hello" + this.a(("World!"))); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/js_with_interpolation b/spec/compilers/js_with_interpolation index fceadb4c8..2ac9f1c92 100644 --- a/spec/compilers/js_with_interpolation +++ b/spec/compilers/js_with_interpolation @@ -10,12 +10,12 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { a() { - return `World!` + return `World!`; } render() { - return ("Hello" + this.a()) + return ("Hello" + this.a()); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/member_access b/spec/compilers/member_access index 678a47fd1..68d1e5794 100644 --- a/spec/compilers/member_access +++ b/spec/compilers/member_access @@ -31,13 +31,13 @@ const A = _R({ "name", Decoder.string ] -}) +}); const C = new(class extends _M { a(b, c) { - return + return; } -}) +}); class B extends _C { render() { @@ -46,11 +46,11 @@ class B extends _C { name: `Joe` }), new A({ name: `Doe` - })]) + })]); - return `asd` - })() + return `asd`; + })(); } -} +}; -B.displayName = "Main" +B.displayName = "Main"; diff --git a/spec/compilers/module b/spec/compilers/module index 7b24e8a0d..1012b60c2 100644 --- a/spec/compilers/module +++ b/spec/compilers/module @@ -16,14 +16,14 @@ const B = new(class extends _M { a() { return _h("p", {}, [ `It should work` - ]) + ]); } -}) +}); class A extends _C { render() { - return B.a() + return B.a(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/module_access b/spec/compilers/module_access index f98c811e5..18f22cb09 100644 --- a/spec/compilers/module_access +++ b/spec/compilers/module_access @@ -19,19 +19,19 @@ component Main { -------------------------------------------------------------------------------- const B = new(class extends _M { c() { - return `Hello` + return `Hello`; } b() { - return B.c + return B.c; } -}) +}); class A extends _C { render() { - let a = B.b() - return a() + let a = B.b(); + return a(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/module_access_get b/spec/compilers/module_access_get index 24289f7e5..afe55394e 100644 --- a/spec/compilers/module_access_get +++ b/spec/compilers/module_access_get @@ -16,23 +16,23 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { render() { - return B.a() + return B.a(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; const B = new(class extends _S { constructor() { - super() - this.state = {} + super(); + this.state = {}; } get b() { - return `Hello` + return `Hello`; } a() { - return B.b + return B.b; } -}) +}); diff --git a/spec/compilers/module_access_subscriptions b/spec/compilers/module_access_subscriptions index d41a72ef3..886ecf4e6 100644 --- a/spec/compilers/module_access_subscriptions +++ b/spec/compilers/module_access_subscriptions @@ -26,17 +26,17 @@ const A = _R({ "test", Decoder.string ] -}) +}); const B = new(class extends _P { a(b) { - return b + return b; } -}) +}); class C extends _C { componentWillUnmount() { - B._unsubscribe(this) + B._unsubscribe(this); } componentDidUpdate() { @@ -46,7 +46,7 @@ class C extends _C { })) } else { B._unsubscribe(this) - } + }; } componentDidMount() { @@ -56,15 +56,15 @@ class C extends _C { })) } else { B._unsubscribe(this) - } + }; } render() { return (() => { - B._subscriptions - return B.a(`a`) - })() + B._subscriptions; + return B.a(`a`); + })(); } -} +}; -C.displayName = "Main" +C.displayName = "Main"; diff --git a/spec/compilers/module_call b/spec/compilers/module_call index 49b746020..414577d8d 100644 --- a/spec/compilers/module_call +++ b/spec/compilers/module_call @@ -16,18 +16,18 @@ component Main { -------------------------------------------------------------------------------- const B = new(class extends _M { b(c) { - return c + return c; } a() { - return B.b(`Lorem ipsum dolor sit amet`) + return B.b(`Lorem ipsum dolor sit amet`); } -}) +}); class A extends _C { render() { - return B.a() + return B.a(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/module_call_piped b/spec/compilers/module_call_piped index 571a59683..7f57941c6 100644 --- a/spec/compilers/module_call_piped +++ b/spec/compilers/module_call_piped @@ -17,18 +17,18 @@ component Main { -------------------------------------------------------------------------------- const B = new(class extends _M { b(c, d) { - return c + return c; } a() { - return B.b(`Lorem ipsum dolor sit amet`, true) + return B.b(`Lorem ipsum dolor sit amet`, true); } -}) +}); class A extends _C { render() { - return B.a() + return B.a(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/next_call b/spec/compilers/next_call index f2eef6813..a379ef4a0 100644 --- a/spec/compilers/next_call +++ b/spec/compilers/next_call @@ -21,20 +21,20 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { constructor(props) { - super(props) + super(props); this.state = new Record({ b: `Joe`, c: 24 - }) + }); } get b() { - return this.state.b + return this.state.b; } get c() { - return this.state.c + return this.state.c; } a() { @@ -43,15 +43,15 @@ class A extends _C { b: `Hello`, c: 30 })), _resolve) - }) + }); } render() { return (() => { - this.a() - return `` - })() + this.a(); + return ``; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/number_literal_negative b/spec/compilers/number_literal_negative index 4e42edab7..bd71d40fc 100644 --- a/spec/compilers/number_literal_negative +++ b/spec/compilers/number_literal_negative @@ -14,15 +14,15 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { a() { - return -42 + return -42; } render() { return (() => { - this.a() - return `` - })() + this.a(); + return ``; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/number_literal_simple b/spec/compilers/number_literal_simple index 6c9b28a72..11f280eb4 100644 --- a/spec/compilers/number_literal_simple +++ b/spec/compilers/number_literal_simple @@ -14,15 +14,15 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { a() { - return 10 + return 10; } render() { return (() => { - this.a() - return `` - })() + this.a(); + return ``; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/number_literal_with_decimal b/spec/compilers/number_literal_with_decimal index d1dc10856..0bb698a32 100644 --- a/spec/compilers/number_literal_with_decimal +++ b/spec/compilers/number_literal_with_decimal @@ -14,15 +14,15 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { a() { - return 10.12 + return 10.12; } render() { return (() => { - this.a() - return `` - })() + this.a(); + return ``; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/operation_chanined b/spec/compilers/operation_chanined index e4c131caf..16bb9d1d4 100644 --- a/spec/compilers/operation_chanined +++ b/spec/compilers/operation_chanined @@ -14,15 +14,15 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { a() { - return _compare(`a`, `b`) && !_compare(true, false) + return _compare(`a`, `b`) && !_compare(true, false); } render() { return (() => { - this.a() - return `` - })() + this.a(); + return ``; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/operation_simple b/spec/compilers/operation_simple index c99e1c6cf..f01fcd5be 100644 --- a/spec/compilers/operation_simple +++ b/spec/compilers/operation_simple @@ -14,15 +14,15 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { a() { - return _compare(`a`, `b`) + return _compare(`a`, `b`); } render() { return (() => { - this.a() - return `` - })() + this.a(); + return ``; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/parallel_simple b/spec/compilers/parallel_simple index 25b87250f..62facb8c2 100644 --- a/spec/compilers/parallel_simple +++ b/spec/compilers/parallel_simple @@ -20,11 +20,11 @@ component Main { class A extends _C { a() { return (async () => { - let _ = null + let _ = null; try { - let b = null - let c = null + let b = null; + let c = null; await Promise.all([ (async () => { @@ -33,26 +33,26 @@ class A extends _C { (async () => { c = await `World` })() - ]) + ]); - _ = b + c + _ = b + c; } catch (_error) { if (!(_error instanceof DoError)) { - console.warn(`Unhandled error in parallel expression:`) - console.warn(_error) + console.warn(`Unhandled error in parallel expression:`); + console.warn(_error); } - } + }; - return _ - })() + return _; + })(); } render() { return (() => { - this.a() - return `` - })() + this.a(); + return ``; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/parallel_with_catch b/spec/compilers/parallel_with_catch index ef13a56a0..db36589ee 100644 --- a/spec/compilers/parallel_with_catch +++ b/spec/compilers/parallel_with_catch @@ -35,68 +35,68 @@ component Main { -------------------------------------------------------------------------------- const B = new(class extends _M { c(h) { - return + return; } -}) +}); class A extends _C { a() { return (async () => { - let _ = null + let _ = null; try { - let b = null - let e = null - let f = null + let b = null; + let e = null; + let f = null; await Promise.all([ (async () => { try { b = await B.c(`x`) } catch (_error) { - let d = _error - _ = `hello` - throw new DoError() + let d = _error; + _ = `hello`; + throw new DoError(); } })(), (async () => { try { e = await B.c(`y`) } catch (_error) { - let d = _error - _ = `hello` - throw new DoError() + let d = _error; + _ = `hello`; + throw new DoError(); } })(), (async () => { try { f = await B.c(0) } catch (_error) { - let g = _error - _ = `asd` - throw new DoError() + let g = _error; + _ = `asd`; + throw new DoError(); } })() - ]) + ]); - _ = `blah` + _ = `blah`; } catch (_error) { if (!(_error instanceof DoError)) { - console.warn(`Unhandled error in parallel expression:`) - console.warn(_error) + console.warn(`Unhandled error in parallel expression:`); + console.warn(_error); } - } + }; - return _ - })() + return _; + })(); } render() { return (() => { - this.a() - return `` - })() + this.a(); + return ``; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/parallel_with_catch_all b/spec/compilers/parallel_with_catch_all index e0699cf80..cc96a1d70 100644 --- a/spec/compilers/parallel_with_catch_all +++ b/spec/compilers/parallel_with_catch_all @@ -33,19 +33,19 @@ component Main { -------------------------------------------------------------------------------- const B = new(class extends _M { c(f) { - return + return; } -}) +}); class A extends _C { a() { return (async () => { - let _ = null + let _ = null; try { - let b = null - let d = null - let e = null + let b = null; + let d = null; + let e = null; await Promise.all([ (async () => { @@ -57,25 +57,25 @@ class A extends _C { (async () => { e = await `World` })() - ]) + ]); - _ = d + e + _ = d + e; } catch (_error) { if (!(_error instanceof DoError)) { return `Hmm...` } - } + }; - return _ - })() + return _; + })(); } render() { return (() => { - this.a() - return `` - })() + this.a(); + return ``; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/parenthesized_expression b/spec/compilers/parenthesized_expression index c9bfdc6da..204643c13 100644 --- a/spec/compilers/parenthesized_expression +++ b/spec/compilers/parenthesized_expression @@ -14,15 +14,15 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { a() { - return (true) + return (true); } render() { return (() => { - this.a() - return `` - })() + this.a(); + return ``; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/partial_application b/spec/compilers/partial_application index 6ef5b907b..4ed565a33 100644 --- a/spec/compilers/partial_application +++ b/spec/compilers/partial_application @@ -16,18 +16,18 @@ component Main { -------------------------------------------------------------------------------- const B = new(class extends _M { b(c, d) { - return c + return c; } a() { - return ((..._) => B.b(`test`, ..._)) + return ((..._) => B.b(`test`, ..._)); } -}) +}); class A extends _C { render() { - return B.a()(true) + return B.a()(true); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/property b/spec/compilers/property index 791e8397e..731cb5fab 100644 --- a/spec/compilers/property +++ b/spec/compilers/property @@ -8,19 +8,19 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { constructor(props) { - super(props) + super(props); this._d({ a: [ null, `Joe` ] - }) + }); } render() { - return _h("div", {}) + return _h("div", {}); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/record b/spec/compilers/record index 7c4015439..8698ba847 100644 --- a/spec/compilers/record +++ b/spec/compilers/record @@ -29,22 +29,22 @@ const A = _R({ "b", Decoder.number ] -}) +}); class B extends _C { a() { return new A({ a: `Hello`, b: 0 - }) + }); } render() { return (() => { - this.a() - return _h("div", {}) - })() + this.a(); + return _h("div", {}); + })(); } -} +}; -B.displayName = "Main" +B.displayName = "Main"; diff --git a/spec/compilers/record_field b/spec/compilers/record_field index 7c4015439..8698ba847 100644 --- a/spec/compilers/record_field +++ b/spec/compilers/record_field @@ -29,22 +29,22 @@ const A = _R({ "b", Decoder.number ] -}) +}); class B extends _C { a() { return new A({ a: `Hello`, b: 0 - }) + }); } render() { return (() => { - this.a() - return _h("div", {}) - })() + this.a(); + return _h("div", {}); + })(); } -} +}; -B.displayName = "Main" +B.displayName = "Main"; diff --git a/spec/compilers/record_update b/spec/compilers/record_update index 6c1a48afb..72ca8d95b 100644 --- a/spec/compilers/record_update +++ b/spec/compilers/record_update @@ -23,11 +23,11 @@ const A = _R({ "name", Decoder.string ] -}) +}); class B extends _C { constructor(props) { - super(props) + super(props); this._d({ b: [ @@ -36,21 +36,21 @@ class B extends _C { name: `Doe` }) ] - }) + }); } a() { return _u(this.b, { name: `John` - }) + }); } render() { return (() => { - this.a() - return _h("div", {}) - })() + this.a(); + return _h("div", {}); + })(); } -} +}; -B.displayName = "Main" +B.displayName = "Main"; diff --git a/spec/compilers/route b/spec/compilers/route index 9068599db..7bd073304 100644 --- a/spec/compilers/route +++ b/spec/compilers/route @@ -17,5 +17,5 @@ _program.addRoutes([ ], path: `/:name` } -]) +]); diff --git a/spec/compilers/sequence_simple b/spec/compilers/sequence_simple index 2735e6c1c..a7cab4958 100644 --- a/spec/compilers/sequence_simple +++ b/spec/compilers/sequence_simple @@ -17,27 +17,27 @@ component Main { class A extends _C { a() { return (async () => { - let _ = null + let _ = null; try { - _ = await null + _ = await null; } catch (_error) { if (!(_error instanceof DoError)) { - console.warn(`Unhandled error in sequence expression:`) - console.warn(_error) + console.warn(`Unhandled error in sequence expression:`); + console.warn(_error); } - } + }; - return _ - })() + return _; + })(); } render() { return (() => { - this.a() - return _h("div", {}) - })() + this.a(); + return _h("div", {}); + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/sequence_using_argument b/spec/compilers/sequence_using_argument index c3e7c30bf..01695f4bf 100644 --- a/spec/compilers/sequence_using_argument +++ b/spec/compilers/sequence_using_argument @@ -19,46 +19,46 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { constructor(props) { - super(props) + super(props); this.state = new Record({ c: `ho` - }) + }); } get c() { - return this.state.c + return this.state.c; } a() { return (async () => { - let _ = null + let _ = null; try { - let b = await `hello` + let b = await `hello`; _ = await new Promise((_resolve) => { this.setState(_u(this.state, new Record({ c: b })), _resolve) - }) + }); } catch (_error) { if (!(_error instanceof DoError)) { - console.warn(`Unhandled error in sequence expression:`) - console.warn(_error) + console.warn(`Unhandled error in sequence expression:`); + console.warn(_error); } - } + }; - return _ - })() + return _; + })(); } render() { return (() => { - this.a() - return _h("div", {}) - })() + this.a(); + return _h("div", {}); + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/sequence_with_argument b/spec/compilers/sequence_with_argument index b1b8a3107..3f19e418a 100644 --- a/spec/compilers/sequence_with_argument +++ b/spec/compilers/sequence_with_argument @@ -18,28 +18,28 @@ component Main { class A extends _C { a() { return (async () => { - let _ = null + let _ = null; try { - let b = await `hello` - _ = await null + let b = await `hello`; + _ = await null; } catch (_error) { if (!(_error instanceof DoError)) { - console.warn(`Unhandled error in sequence expression:`) - console.warn(_error) + console.warn(`Unhandled error in sequence expression:`); + console.warn(_error); } - } + }; - return _ - })() + return _; + })(); } render() { return (() => { - this.a() - return _h("div", {}) - })() + this.a(); + return _h("div", {}); + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/sequence_with_catch b/spec/compilers/sequence_with_catch index 9676ca56b..6650eba68 100644 --- a/spec/compilers/sequence_with_catch +++ b/spec/compilers/sequence_with_catch @@ -35,64 +35,64 @@ component Main { -------------------------------------------------------------------------------- const B = new(class extends _M { c(h) { - return + return; } -}) +}); class A extends _C { a() { return (async () => { - let _ = null + let _ = null; try { let b = await (async () => { try { return await B.c(`x`) } catch (_error) { - let d = _error - _ = d - throw new DoError() + let d = _error; + _ = d; + throw new DoError(); } - })() + })(); let e = await (async () => { try { return await B.c(`y`) } catch (_error) { - let d = _error - _ = d - throw new DoError() + let d = _error; + _ = d; + throw new DoError(); } - })() + })(); let f = await (async () => { try { return await B.c(0) } catch (_error) { - let g = _error - _ = `asd` - throw new DoError() + let g = _error; + _ = `asd`; + throw new DoError(); } - })() + })(); - _ = await `blah` + _ = await `blah`; } catch (_error) { if (!(_error instanceof DoError)) { - console.warn(`Unhandled error in sequence expression:`) - console.warn(_error) + console.warn(`Unhandled error in sequence expression:`); + console.warn(_error); } - } + }; - return _ - })() + return _; + })(); } render() { return (() => { - this.a() - return _h("div", {}) - })() + this.a(); + return _h("div", {}); + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/sequence_with_finally b/spec/compilers/sequence_with_finally index e0e6bfd5a..8d6ed4a7a 100644 --- a/spec/compilers/sequence_with_finally +++ b/spec/compilers/sequence_with_finally @@ -19,29 +19,29 @@ component Main { class A extends _C { a() { return (async () => { - let _ = null + let _ = null; try { - _ = await null + _ = await null; } catch (_error) { if (!(_error instanceof DoError)) { - console.warn(`Unhandled error in sequence expression:`) - console.warn(_error) + console.warn(`Unhandled error in sequence expression:`); + console.warn(_error); } } finally { null - } + }; - return _ - })() + return _; + })(); } render() { return (() => { - this.a() - return _h("div", {}) - })() + this.a(); + return _h("div", {}); + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/sequence_with_result_and_catch b/spec/compilers/sequence_with_result_and_catch index 6ba4a9d8d..02194c1b0 100644 --- a/spec/compilers/sequence_with_result_and_catch +++ b/spec/compilers/sequence_with_result_and_catch @@ -26,46 +26,46 @@ component Main { -------------------------------------------------------------------------------- const B = new(class extends _M { b(d) { - return (new Err(arguments[0])) + return (new Err(arguments[0])); } -}) +}); class A extends _C { a() { return (async () => { - let _ = null + let _ = null; try { - let _0 = B.b(``) + let _0 = B.b(``); if (_0 instanceof Err) { - let _error = _0._0 + let _error = _0._0; - let c = _error - _ = `test` - throw new DoError() - } + let c = _error; + _ = `test`; + throw new DoError(); + }; - _0._0 + _0._0; - _ = await `test` + _ = await `test`; } catch (_error) { if (!(_error instanceof DoError)) { - console.warn(`Unhandled error in sequence expression:`) - console.warn(_error) + console.warn(`Unhandled error in sequence expression:`); + console.warn(_error); } - } + }; - return _ - })() + return _; + })(); } render() { return (() => { - this.a() - return _h("div", {}) - })() + this.a(); + return _h("div", {}); + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/sequence_with_result_and_catch_all b/spec/compilers/sequence_with_result_and_catch_all index f421b208f..233df4e1c 100644 --- a/spec/compilers/sequence_with_result_and_catch_all +++ b/spec/compilers/sequence_with_result_and_catch_all @@ -26,41 +26,41 @@ component Main { -------------------------------------------------------------------------------- const B = new(class extends _M { b(c) { - return (new Err(c)) + return (new Err(c)); } -}) +}); class A extends _C { a() { return (async () => { - let _ = null + let _ = null; try { - let _0 = B.b(``) + let _0 = B.b(``); if (_0 instanceof Err) { throw _0._0 - } + }; - _0._0 + _0._0; - _ = await `test` + _ = await `test`; } catch (_error) { if (!(_error instanceof DoError)) { _ = `test` } - } + }; - return _ - })() + return _; + })(); } render() { return (() => { - this.a() - return _h("div", {}) - })() + this.a(); + return _h("div", {}); + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/state b/spec/compilers/state index 891c32d02..8c7858409 100644 --- a/spec/compilers/state +++ b/spec/compilers/state @@ -13,29 +13,29 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { constructor(props) { - super(props) + super(props); this.state = new Record({ b: `Hello`, c: `0` - }) + }); } get b() { - return this.state.b + return this.state.b; } get c() { - return this.state.c + return this.state.c; } a() { - return this.b + this.c + return this.b + this.c; } render() { - return this.a() + return this.a(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/statement b/spec/compilers/statement index 77d501e25..446ad039f 100644 --- a/spec/compilers/statement +++ b/spec/compilers/statement @@ -12,10 +12,10 @@ component Main { class A extends _C { render() { return (() => { - let a = `hello` - return a - })() + let a = `hello`; + return a; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/store b/spec/compilers/store index f29f8340e..f24b8a744 100644 --- a/spec/compilers/store +++ b/spec/compilers/store @@ -16,38 +16,38 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { get a() { - return B.b + return B.b; } componentWillUnmount() { - B._unsubscribe(this) + B._unsubscribe(this); } componentDidMount() { - B._subscribe(this) + B._subscribe(this); } render() { - return this.a + return this.a; } -} +}; -A.displayName = "Main" +A.displayName = "Main"; const B = new(class extends _S { constructor() { - super() + super(); this.state = { b: `` - } + }; } get b() { - return this.state.b + return this.state.b; } c() { - return `hello` + return `hello`; } -}) +}); diff --git a/spec/compilers/store_with_get b/spec/compilers/store_with_get index 64b1a45d6..67858cd72 100644 --- a/spec/compilers/store_with_get +++ b/spec/compilers/store_with_get @@ -16,38 +16,38 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { get a() { - return B.b + return B.b; } componentWillUnmount() { - B._unsubscribe(this) + B._unsubscribe(this); } componentDidMount() { - B._subscribe(this) + B._subscribe(this); } render() { - return this.a + return this.a; } -} +}; -A.displayName = "Main" +A.displayName = "Main"; const B = new(class extends _S { constructor() { - super() + super(); this.state = { b: `` - } + }; } get b() { - return this.state.b + return this.state.b; } get c() { - return `hello` + return `hello`; } -}) +}); diff --git a/spec/compilers/string_literal_escaped b/spec/compilers/string_literal_escaped index e1917867d..dd1f54844 100644 --- a/spec/compilers/string_literal_escaped +++ b/spec/compilers/string_literal_escaped @@ -6,8 +6,8 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { render() { - return `Hello There "Joe"` + return `Hello There "Joe"`; } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/string_literal_simple b/spec/compilers/string_literal_simple index 3d5a056f3..bc3217f7e 100644 --- a/spec/compilers/string_literal_simple +++ b/spec/compilers/string_literal_simple @@ -6,8 +6,8 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { render() { - return `Hello There` + return `Hello There`; } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/string_literal_with_backtick b/spec/compilers/string_literal_with_backtick index b516511ad..bbafeb2be 100644 --- a/spec/compilers/string_literal_with_backtick +++ b/spec/compilers/string_literal_with_backtick @@ -6,8 +6,8 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { render() { - return `Hello There \`Joe\`` + return `Hello There \`Joe\``; } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/try b/spec/compilers/try index 24a525a5f..c5ef95f26 100644 --- a/spec/compilers/try +++ b/spec/compilers/try @@ -9,9 +9,9 @@ component Main { class A extends _C { render() { return (() => { - return `hello` - })() + return `hello`; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/try_with_catch_all b/spec/compilers/try_with_catch_all index 762e16805..ba36a0af6 100644 --- a/spec/compilers/try_with_catch_all +++ b/spec/compilers/try_with_catch_all @@ -19,9 +19,9 @@ component Main { -------------------------------------------------------------------------------- const B = new(class extends _M { b(c) { - return (new Err(c)) + return (new Err(c)); } -}) +}); class A extends _C { render() { @@ -30,18 +30,18 @@ class A extends _C { return `Blah` } - let _0 = B.b(`Blah`) + let _0 = B.b(`Blah`); if (_0 instanceof Err) { - let _error = _0._0 - return _catch_all() - } + let _error = _0._0; + return _catch_all(); + }; - let a = _0._0 + let a = _0._0; - return `Hello` - })() + return `Hello`; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/try_with_catches b/spec/compilers/try_with_catches index 7713bf4fd..f2153704f 100644 --- a/spec/compilers/try_with_catches +++ b/spec/compilers/try_with_catches @@ -19,27 +19,27 @@ component Main { -------------------------------------------------------------------------------- const B = new(class extends _M { b(d) { - return (new Err(d)) + return (new Err(d)); } -}) +}); class A extends _C { render() { return (() => { - let _0 = B.b(`Blah`) + let _0 = B.b(`Blah`); if (_0 instanceof Err) { - let _error = _0._0 + let _error = _0._0; - let c = _error - return c - } + let c = _error; + return c; + }; - let a = _0._0 + let a = _0._0; - return a - })() + return a; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/try_with_statement b/spec/compilers/try_with_statement index 77d501e25..446ad039f 100644 --- a/spec/compilers/try_with_statement +++ b/spec/compilers/try_with_statement @@ -12,10 +12,10 @@ component Main { class A extends _C { render() { return (() => { - let a = `hello` - return a - })() + let a = `hello`; + return a; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/variable_argument b/spec/compilers/variable_argument index af8582607..635bc60b4 100644 --- a/spec/compilers/variable_argument +++ b/spec/compilers/variable_argument @@ -10,12 +10,12 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { a(b) { - return b + return b; } render() { - return this.a(`X`) + return this.a(`X`); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/variable_component_function b/spec/compilers/variable_component_function index fad4627c9..35f8d6977 100644 --- a/spec/compilers/variable_component_function +++ b/spec/compilers/variable_component_function @@ -10,12 +10,12 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { a() { - return `Hello` + return `Hello`; } render() { - return this.a() + return this.a(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/variable_component_get b/spec/compilers/variable_component_get index 5414c9837..34d2108eb 100644 --- a/spec/compilers/variable_component_get +++ b/spec/compilers/variable_component_get @@ -10,12 +10,12 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { get a() { - return `Hello` + return `Hello`; } render() { - return this.a + return this.a; } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/variable_component_property b/spec/compilers/variable_component_property index 9a4ce4338..f249931f8 100644 --- a/spec/compilers/variable_component_property +++ b/spec/compilers/variable_component_property @@ -8,19 +8,19 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { constructor(props) { - super(props) + super(props); this._d({ a: [ null, `Hello` ] - }) + }); } render() { - return this.a + return this.a; } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/variable_module_function b/spec/compilers/variable_module_function index a9fbb4c87..e12495b01 100644 --- a/spec/compilers/variable_module_function +++ b/spec/compilers/variable_module_function @@ -18,20 +18,20 @@ component Main { -------------------------------------------------------------------------------- const B = new(class extends _M { c() { - return `Hello` + return `Hello`; } b() { - return B.c + return B.c; } -}) +}); class A extends _C { render() { - let a = B.b() - return a() + let a = B.b(); + return a(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/variable_where b/spec/compilers/variable_where index b1b892b92..748d73cb8 100644 --- a/spec/compilers/variable_where +++ b/spec/compilers/variable_where @@ -13,14 +13,14 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { a() { - let b = `Hello` - return b + let b = `Hello`; + return b; } render() { - return this.a() + return this.a(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/void b/spec/compilers/void index 2044b60df..5b318e585 100644 --- a/spec/compilers/void +++ b/spec/compilers/void @@ -14,15 +14,15 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { a() { - return null + return null; } render() { return (() => { - let b = this.a - return `` - })() + let b = this.a; + return ``; + })(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/where b/spec/compilers/where index a924736e1..fc8d237fd 100644 --- a/spec/compilers/where +++ b/spec/compilers/where @@ -18,16 +18,16 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { a() { - let b = `Asd` + let b = `Asd`; return _h("div", {}, [ b - ]) + ]); } render() { - return this.a() + return this.a(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/spec/compilers/with b/spec/compilers/with index aaced2e13..d732b93b1 100644 --- a/spec/compilers/with +++ b/spec/compilers/with @@ -14,14 +14,14 @@ component Main { -------------------------------------------------------------------------------- const B = new(class extends _M { a() { - return `` + return ``; } -}) +}); class A extends _C { render() { - return B.a() + return B.a(); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; diff --git a/src/compilers/component.cr b/src/compilers/component.cr index e6aa7cd34..644ec189a 100644 --- a/src/compilers/component.cr +++ b/src/compilers/component.cr @@ -86,11 +86,11 @@ module Mint name = js.variable_of(key) if store.states.find(&.name.value.==(original)) - memo << js.get(name, "return #{store_name}.#{id}") + memo << js.get(name, "return #{store_name}.#{id};") elsif store.gets.any? { |get| get.name.value == original } - memo << js.get(name, "return #{store_name}.#{id}") + memo << js.get(name, "return #{store_name}.#{id};") elsif store.functions.any? { |func| func.name.value == original } - memo << "#{name} (...params) { return #{store_name}.#{id}(...params) }" + memo << "#{name} (...params) { return #{store_name}.#{id}(...params); }" end end end diff --git a/src/compilers/property.cr b/src/compilers/property.cr index 5e1a76521..eee82642e 100644 --- a/src/compilers/property.cr +++ b/src/compilers/property.cr @@ -12,7 +12,7 @@ module Mint js.variable_of(node) body = - "return this._p('#{prop_name}')" + "return this._p('#{prop_name}');" js.get(name, body) end diff --git a/src/compilers/state.cr b/src/compilers/state.cr index 66a48e09d..e9b22b2f4 100644 --- a/src/compilers/state.cr +++ b/src/compilers/state.cr @@ -4,7 +4,7 @@ module Mint name = js.variable_of(node) - js.get(name, "return this.state.#{name}") + js.get(name, "return this.state.#{name};") end end end diff --git a/src/js.cr b/src/js.cr index 839b46fa3..61bed0e1d 100644 --- a/src/js.cr +++ b/src/js.cr @@ -204,6 +204,7 @@ module Mint end memo += item + memo += ";" unless memo.ends_with?(";") memo end end