diff --git a/spec/compilers/component_namespaced_with_style b/spec/compilers/component_namespaced_with_style new file mode 100644 index 000000000..d7093529d --- /dev/null +++ b/spec/compilers/component_namespaced_with_style @@ -0,0 +1,45 @@ +component Ui.Dropdown { + style base { + background: red; + } + + fun render : Html { + + "test" + + } +} + +component Main { + fun render : Html { + + } +} +-------------------------------------------------------------------------------- +class A extends _C { + render() { + return _h("div", { + className: `Ui·Dropdown_base` + }, [ + `test` + ]); + } +}; + +A.displayName = "Ui.Dropdown"; + +class B extends _C { + render() { + return $a(); + } +}; + +B.displayName = "Main"; + +const $a = _m(() => _h(A, {})); + +_insertStyles(` +.Ui·Dropdown_base { + background: red; +} +`); diff --git a/spec/compilers/css_definition b/spec/compilers/css_definition index 4a9925a08..71fa50265 100644 --- a/spec/compilers/css_definition +++ b/spec/compilers/css_definition @@ -13,7 +13,7 @@ component Main { } -------------------------------------------------------------------------------- class A extends _C { - $a() { + $Main_test() { const _ = { [`--a-a`]: this.a + `px 0px` }; @@ -27,8 +27,8 @@ class A extends _C { render() { return _h("div", { - className: `a`, - style: _style([this.$a()]) + className: `Main_test`, + style: _style([this.$Main_test()]) }); } }; @@ -36,7 +36,7 @@ class A extends _C { A.displayName = "Main"; _insertStyles(` -.a { +.Main_test { margin: var(--a-a); } `); diff --git a/spec/compilers/css_font_face b/spec/compilers/css_font_face index ed18d6aed..13d2144bf 100644 --- a/spec/compilers/css_font_face +++ b/spec/compilers/css_font_face @@ -20,7 +20,7 @@ component Main { class A extends _C { render() { return _h("div", { - className: `a` + className: `Main_test` }); } }; diff --git a/spec/compilers/css_font_face_with_qoutes b/spec/compilers/css_font_face_with_qoutes index dd3ba6f1a..2533cfe7b 100644 --- a/spec/compilers/css_font_face_with_qoutes +++ b/spec/compilers/css_font_face_with_qoutes @@ -16,7 +16,7 @@ component Main { class A extends _C { render() { return _h("div", { - className: `a` + className: `Main_test` }); } }; @@ -24,7 +24,7 @@ class A extends _C { A.displayName = "Main"; _insertStyles(` -.a { +.Main_test { font-family: "myFirstFont"; } diff --git a/spec/compilers/css_keyframes b/spec/compilers/css_keyframes index b39b9c02f..64d3473e9 100644 --- a/spec/compilers/css_keyframes +++ b/spec/compilers/css_keyframes @@ -37,7 +37,7 @@ class A extends _C { }); } - $a() { + $Main_test() { const _ = { [`--a-a`]: this.a }; @@ -51,8 +51,8 @@ class A extends _C { render() { return _h("div", { - className: `a`, - style: _style([this.$a()]) + className: `Main_test`, + style: _style([this.$Main_test()]) }); } }; diff --git a/spec/compilers/css_media b/spec/compilers/css_media index 2ff155219..ce83643e2 100644 --- a/spec/compilers/css_media +++ b/spec/compilers/css_media @@ -19,7 +19,7 @@ component Main { } -------------------------------------------------------------------------------- class A extends _C { - $a() { + $Main_test() { const _ = { [`--a-a`]: this.a, [`--b-a`]: this.a @@ -34,8 +34,8 @@ class A extends _C { render() { return _h("div", { - className: `a`, - style: _style([this.$a()]) + className: `Main_test`, + style: _style([this.$Main_test()]) }); } }; @@ -43,12 +43,12 @@ class A extends _C { A.displayName = "Main"; _insertStyles(` -.a div { +.Main_test div { color: var(--a-a); } @media (screen) { - .a { + .Main_test { color: var(--b-a); } } diff --git a/spec/compilers/css_media_with_if b/spec/compilers/css_media_with_if index e3a94053a..c0cd784ca 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() { + $Main_test() { const _ = {}; (true ? Object.assign(_, { @@ -27,8 +27,8 @@ class A extends _C { render() { return _h("div", { - className: `a`, - style: _style([this.$a()]) + className: `Main_test`, + style: _style([this.$Main_test()]) }); } }; @@ -36,12 +36,12 @@ class A extends _C { A.displayName = "Main"; _insertStyles(` -.a { +.Main_test { color: yellow; } @media (max-width: 300px) { - .a { + .Main_test { color: var(--a-a); } } diff --git a/spec/compilers/css_selector b/spec/compilers/css_selector index 5302ec8a5..453dafe46 100644 --- a/spec/compilers/css_selector +++ b/spec/compilers/css_selector @@ -19,7 +19,7 @@ component Main { } -------------------------------------------------------------------------------- class A extends _C { - $a() { + $Main_test() { const _ = { [`--a-a`]: this.a }; @@ -33,8 +33,8 @@ class A extends _C { render() { return _h("div", { - className: `a`, - style: _style([this.$a()]) + className: `Main_test`, + style: _style([this.$Main_test()]) }); } }; @@ -42,11 +42,11 @@ class A extends _C { A.displayName = "Main"; _insertStyles(` -.a div { +.Main_test div { color: var(--a-a); } -.a:focus { +.Main_test:focus { color: red; } `); diff --git a/spec/compilers/css_selector_multiple b/spec/compilers/css_selector_multiple index b90d632cd..bb4970284 100644 --- a/spec/compilers/css_selector_multiple +++ b/spec/compilers/css_selector_multiple @@ -20,7 +20,7 @@ component Main { } -------------------------------------------------------------------------------- class A extends _C { - $a() { + $Main_test() { const _ = { [`--a-a`]: this.a }; @@ -34,8 +34,8 @@ class A extends _C { render() { return _h("div", { - className: `a`, - style: _style([this.$a()]) + className: `Main_test`, + style: _style([this.$Main_test()]) }); } }; @@ -43,15 +43,15 @@ class A extends _C { A.displayName = "Main"; _insertStyles(` -.a div { +.Main_test div { color: var(--a-a); } -.a:focus { +.Main_test:focus { color: red; } -.a:hover { +.Main_test:hover { color: red; } `); diff --git a/spec/compilers/css_string b/spec/compilers/css_string index 5717b8d69..e0882adef 100644 --- a/spec/compilers/css_string +++ b/spec/compilers/css_string @@ -23,7 +23,7 @@ class A extends _C { }); } - $a() { + $Main_unicode() { const _ = { [`--a-a`]: `"Hi"` + ` blah ` + this.a + ` ` + `"Here is some content; Thanks ${this.a}"` }; @@ -37,8 +37,8 @@ class A extends _C { render() { return _h("div", { - className: `a`, - style: _style([this.$a()]) + className: `Main_unicode`, + style: _style([this.$Main_unicode()]) }, [ _h("span", {}) ]); @@ -48,7 +48,7 @@ class A extends _C { A.displayName = "Main"; _insertStyles(` -.a span::after { +.Main_unicode span::after { content: var(--a-a); } `); diff --git a/spec/compilers/css_supports b/spec/compilers/css_supports index 26b707157..824a44f13 100644 --- a/spec/compilers/css_supports +++ b/spec/compilers/css_supports @@ -21,7 +21,7 @@ class A extends _C { }); } - $a() { + $Main_test() { const _ = { [`--a-a`]: this.a }; @@ -35,8 +35,8 @@ class A extends _C { render() { return _h("div", { - className: `a`, - style: _style([this.$a()]) + className: `Main_test`, + style: _style([this.$Main_test()]) }); } }; @@ -45,7 +45,7 @@ A.displayName = "Main"; _insertStyles(` @supports (screen) { - .a { + .Main_test { color: var(--a-a); } } diff --git a/spec/compilers/css_with_ands b/spec/compilers/css_with_ands index 84fe8b42d..d1b2c41ac 100644 --- a/spec/compilers/css_with_ands +++ b/spec/compilers/css_with_ands @@ -21,7 +21,7 @@ component Main { class A extends _C { render() { return _h("div", { - className: `a` + className: `Main_test` }); } }; @@ -29,15 +29,15 @@ class A extends _C { A.displayName = "Main"; _insertStyles(` -.a:focus { +.Main_test:focus { color: red; } -.a[someattribute] { +.Main_test[someattribute] { color: red; } -.a.someclass { +.Main_test.someclass { color: red; } `); diff --git a/spec/compilers/css_with_arguments b/spec/compilers/css_with_arguments index 39d38fcb4..ef663a97d 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) { + $Main_test(a) { const _ = { [`--a-a`]: a }; @@ -19,8 +19,8 @@ class A extends _C { render() { return _h("div", { - className: `a`, - style: _style([this.$a(`red`)]) + className: `Main_test`, + style: _style([this.$Main_test(`red`)]) }); } }; @@ -28,7 +28,7 @@ class A extends _C { A.displayName = "Main"; _insertStyles(` -.a { +.Main_test { color: var(--a-a); } `); diff --git a/spec/compilers/css_with_case b/spec/compilers/css_with_case index fa9eb02c4..2f731c50d 100644 --- a/spec/compilers/css_with_case +++ b/spec/compilers/css_with_case @@ -17,7 +17,7 @@ component Main { } -------------------------------------------------------------------------------- class A extends _C { - $a() { + $Main_test() { const _ = {}; (() => { @@ -39,8 +39,8 @@ class A extends _C { render() { return _h("div", { - className: `a`, - style: _style([this.$a()]) + className: `Main_test`, + style: _style([this.$Main_test()]) }); } }; @@ -48,7 +48,7 @@ class A extends _C { A.displayName = "Main"; _insertStyles(` -.a { +.Main_test { color: var(--a-a, yellow); } `); diff --git a/spec/compilers/css_with_else_if b/spec/compilers/css_with_else_if index 0e5bc11c1..e6bc7f97e 100644 --- a/spec/compilers/css_with_else_if +++ b/spec/compilers/css_with_else_if @@ -18,7 +18,7 @@ component Main { } -------------------------------------------------------------------------------- class A extends _C { - $a(a) { + $Main_base(a) { const _ = {}; (_compare(a, `red`) ? Object.assign(_, { @@ -33,8 +33,8 @@ class A extends _C { render() { return _h("div", {}, [ _h("div", { - className: `a`, - style: _style([this.$a(`blue`)]) + className: `Main_base`, + style: _style([this.$Main_base(`blue`)]) }) ]); } @@ -43,7 +43,7 @@ class A extends _C { A.displayName = "Main"; _insertStyles(` -.a { +.Main_base { height: 20px; background: var(--a-a); } diff --git a/spec/compilers/css_with_if b/spec/compilers/css_with_if index 8b16632da..5359ae556 100644 --- a/spec/compilers/css_with_if +++ b/spec/compilers/css_with_if @@ -15,7 +15,7 @@ component Main { } -------------------------------------------------------------------------------- class A extends _C { - $a() { + $Main_test() { const _ = {}; (true ? Object.assign(_, { @@ -29,8 +29,8 @@ class A extends _C { render() { return _h("div", { - className: `a`, - style: _style([this.$a()]) + className: `Main_test`, + style: _style([this.$Main_test()]) }); } }; @@ -38,7 +38,7 @@ class A extends _C { A.displayName = "Main"; _insertStyles(` -.a { +.Main_test { color: var(--a-a, yellow); } `); diff --git a/spec/compilers/css_with_if_and_case b/spec/compilers/css_with_if_and_case index 82ccb00e9..e35a193f8 100644 --- a/spec/compilers/css_with_if_and_case +++ b/spec/compilers/css_with_if_and_case @@ -20,7 +20,7 @@ component Main { } -------------------------------------------------------------------------------- class A extends _C { - $a() { + $Main_test() { const _ = {}; (() => { @@ -48,8 +48,8 @@ class A extends _C { render() { return _h("div", { - className: `a`, - style: _style([this.$a()]) + className: `Main_test`, + style: _style([this.$Main_test()]) }); } }; @@ -57,7 +57,7 @@ class A extends _C { A.displayName = "Main"; _insertStyles(` -.a { +.Main_test { color: var(--a-a, yellow); } `); diff --git a/spec/compilers/css_with_if_same_condition b/spec/compilers/css_with_if_same_condition index 1a371b9ca..9400b70ee 100644 --- a/spec/compilers/css_with_if_same_condition +++ b/spec/compilers/css_with_if_same_condition @@ -23,7 +23,7 @@ component Main { } -------------------------------------------------------------------------------- class A extends _C { - $a() { + $Main_test() { const _ = {}; (true ? Object.assign(_, { @@ -43,8 +43,8 @@ class A extends _C { render() { return _h("div", { - className: `a`, - style: _style([this.$a()]) + className: `Main_test`, + style: _style([this.$Main_test()]) }); } }; @@ -52,11 +52,11 @@ class A extends _C { A.displayName = "Main"; _insertStyles(` -.a .tag { +.Main_test .tag { color: var(--a-a); } -.a .string { +.Main_test .string { color: var(--b-a); } `); diff --git a/spec/compilers/html_attribute_class_with_style b/spec/compilers/html_attribute_class_with_style index b826f2e05..623eecfd0 100644 --- a/spec/compilers/html_attribute_class_with_style +++ b/spec/compilers/html_attribute_class_with_style @@ -12,7 +12,7 @@ component Main { class A extends _C { render() { return _h("div", { - className: `something` + ` a` + className: `something` + ` Main_base` }); } }; @@ -20,7 +20,7 @@ class A extends _C { A.displayName = "Main"; _insertStyles(` -.a { +.Main_base { width: 100%; } `); diff --git a/spec/compilers/html_with_multiple_styles b/spec/compilers/html_with_multiple_styles index 7eaaad503..ee6ab1281 100644 --- a/spec/compilers/html_with_multiple_styles +++ b/spec/compilers/html_with_multiple_styles @@ -16,7 +16,7 @@ component Main { class A extends _C { render() { return _h("div", { - className: `a b` + className: `Main_one Main_two` }); } }; @@ -24,11 +24,11 @@ class A extends _C { A.displayName = "Main"; _insertStyles(` -.a { +.Main_one { color: red; } -.b { +.Main_two { color: blue; } `); diff --git a/spec/compilers/html_with_multiple_styles_and_parameters b/spec/compilers/html_with_multiple_styles_and_parameters index 79e463802..372d05474 100644 --- a/spec/compilers/html_with_multiple_styles_and_parameters +++ b/spec/compilers/html_with_multiple_styles_and_parameters @@ -14,7 +14,7 @@ component Main { } -------------------------------------------------------------------------------- class A extends _C { - $b(a) { + $Main_two(a) { const _ = { [`--a-a`]: a }; @@ -24,8 +24,8 @@ class A extends _C { render() { return _h("div", { - className: `a b`, - style: _style([this.$b(`blue`)]) + className: `Main_one Main_two`, + style: _style([this.$Main_two(`blue`)]) }); } }; @@ -33,11 +33,11 @@ class A extends _C { A.displayName = "Main"; _insertStyles(` -.a { +.Main_one { color: red; } -.b { +.Main_two { color: var(--a-a); } `); diff --git a/spec/compilers/html_with_multiple_styles_and_parameters_2 b/spec/compilers/html_with_multiple_styles_and_parameters_2 index 82423f833..3d24d6147 100644 --- a/spec/compilers/html_with_multiple_styles_and_parameters_2 +++ b/spec/compilers/html_with_multiple_styles_and_parameters_2 @@ -14,7 +14,7 @@ component Main { } -------------------------------------------------------------------------------- class A extends _C { - $a(a) { + $Main_one(a) { const _ = { [`--a-a`]: a }; @@ -22,7 +22,7 @@ class A extends _C { return _; } - $b(b) { + $Main_two(b) { const _ = { [`--b-a`]: b }; @@ -32,8 +32,8 @@ class A extends _C { render() { return _h("div", { - className: `a b`, - style: _style([this.$a(`red`), this.$b(`blue`)]) + className: `Main_one Main_two`, + style: _style([this.$Main_one(`red`), this.$Main_two(`blue`)]) }); } }; @@ -41,11 +41,11 @@ class A extends _C { A.displayName = "Main"; _insertStyles(` -.a { +.Main_one { color: var(--a-a); } -.b { +.Main_two { color: var(--b-a); } `); diff --git a/spec/compilers/html_with_pseudos b/spec/compilers/html_with_pseudos index ac4453895..f58a75f01 100644 --- a/spec/compilers/html_with_pseudos +++ b/spec/compilers/html_with_pseudos @@ -33,7 +33,7 @@ class A extends _C { }); } - $a() { + $Main_test() { const _ = { [`--a-a`]: this.a, [`--b-a`]: this.b, @@ -53,8 +53,8 @@ class A extends _C { render() { return _h("div", { - className: `a`, - style: _style([this.$a()]) + className: `Main_test`, + style: _style([this.$Main_test()]) }); } }; @@ -62,17 +62,17 @@ class A extends _C { A.displayName = "Main"; _insertStyles(` -.a { +.Main_test { background: var(--a-a); color: red; } -.a:hover { +.Main_test:hover { background: var(--b-a); color: cyan; } -.a div { +.Main_test div { font-family: var(--c-a); color: blue; } diff --git a/spec/compilers/html_with_style b/spec/compilers/html_with_style index 0494caec4..8763cfbcb 100644 --- a/spec/compilers/html_with_style +++ b/spec/compilers/html_with_style @@ -28,7 +28,7 @@ class A extends _C { }); } - $a() { + $Main_test() { const _ = { [`--a-a`]: this.a, [`--a-b`]: this.a, @@ -49,8 +49,8 @@ class A extends _C { render() { return _h("div", { - className: `a`, - style: _style([this.$a()]) + className: `Main_test`, + style: _style([this.$Main_test()]) }); } }; @@ -58,7 +58,7 @@ class A extends _C { A.displayName = "Main"; _insertStyles(` -.a { +.Main_test { -webkit-tap-highlight-color: rgba(0,0,0,0); -webkit-touch-callout: none; border-color: var(--a-a); diff --git a/spec/compilers/html_with_style_and_custom_style b/spec/compilers/html_with_style_and_custom_style index 67e6687eb..fa2addc33 100644 --- a/spec/compilers/html_with_style_and_custom_style +++ b/spec/compilers/html_with_style_and_custom_style @@ -25,7 +25,7 @@ class A extends _C { }); } - $a() { + $Main_test() { const _ = { [`--a-a`]: this.a }; @@ -43,8 +43,8 @@ class A extends _C { render() { return _h("div", { - className: `a`, - style: _style([this.$a(), this.b]) + className: `Main_test`, + style: _style([this.$Main_test(), this.b]) }); } }; @@ -52,7 +52,7 @@ class A extends _C { A.displayName = "Main"; _insertStyles(` -.a { +.Main_test { background: var(--a-a); color: red; } diff --git a/spec/style_builder_spec.cr b/spec/style_builder_spec.cr index 1eb6e522f..5c02b0b99 100644 --- a/spec/style_builder_spec.cr +++ b/spec/style_builder_spec.cr @@ -60,10 +60,33 @@ describe Mint::StyleBuilder do style = parser.style.should_not be_nil - builder = Mint::StyleBuilder.new(css_prefix: "foo-") - builder.process(style) + builder = Mint::StyleBuilder.new(css_prefix: "foo_") + builder.process(style, "HASH_ID") compiled = builder.compile - compiled.should contain(".foo-a div span pre a {") + compiled.should contain(".foo_HASH_ID_test div span pre a {") + end + + it "optimizes class names if optimize is set" do + example = + <<-MINT + style test { + div { + background: red; + } + } + MINT + + parser = + Mint::Parser.new(example.strip, "test.mint") + + style = + parser.style.should_not be_nil + + builder = Mint::StyleBuilder.new(optimize: true) + builder.process(style, "HASH_ID") + + compiled = builder.compile + compiled.should contain(".a div {") end end diff --git a/src/compiler.cr b/src/compiler.cr index e8ce19f42..66b19e2b6 100644 --- a/src/compiler.cr +++ b/src/compiler.cr @@ -14,7 +14,7 @@ module Mint def initialize(@artifacts : TypeChecker::Artifacts, @optimize = false, css_prefix = nil, @relative = false, @build = false) @style_builder = - StyleBuilder.new(css_prefix: css_prefix) + StyleBuilder.new(css_prefix: css_prefix, optimize: @optimize) @js = Js.new(optimize: @optimize) diff --git a/src/compilers/html_element.cr b/src/compilers/html_element.cr index d66eb8251..10edcd350 100644 --- a/src/compilers/html_element.cr +++ b/src/compilers/html_element.cr @@ -46,7 +46,7 @@ module Mint .reduce({} of String => String) { |memo, item| memo.merge(item) } style_nodes = - node.styles.map { |item| lookups[item] } + node.styles.map { |item| lookups[item].as(Ast::Style) } class_name = unless style_nodes.empty? @@ -89,7 +89,7 @@ module Mint compile item.arguments style_name = - style_builder.style_pool.of(lookups[item], nil) + style_builder.style_pool.of(lookups[item].as(Ast::Style), nil) styles << js.call("this.$#{style_name}", arguments) end diff --git a/src/compilers/style.cr b/src/compilers/style.cr index 34abf374a..21035d56c 100644 --- a/src/compilers/style.cr +++ b/src/compilers/style.cr @@ -15,7 +15,7 @@ module Mint end def _compile(node : Ast::Style, component : Ast::Component) : Nil - style_builder.process node + style_builder.process(node, component.name.gsub('.', '·')) end end end diff --git a/src/compilers/top_level.cr b/src/compilers/top_level.cr index 1f0739611..1fcc95ecc 100644 --- a/src/compilers/top_level.cr +++ b/src/compilers/top_level.cr @@ -195,6 +195,13 @@ module Mint from_event_call = "#{js.class_of(html_event_module)}.#{js.variable_of(from_event)}" + minimized_class_warning = + unless build + <<-JS + console.warn("%c!!!DO NOT TARGET ELEMENTS WITH SELECTORS BECAUSE THE SELECTORS WILL BE MINIMIZED IN THE PRODUCTION BUILD!!!", "font-size: 2em") + JS + end + <<-RESULT (() => { const _enums = {} @@ -265,6 +272,7 @@ module Mint _enums.err = #{err} _enums.ok = #{ok} + #{minimized_class_warning} #{main} })() RESULT diff --git a/src/style_builder.cr b/src/style_builder.cr index 858732c66..d3786f8f0 100644 --- a/src/style_builder.cr +++ b/src/style_builder.cr @@ -17,6 +17,28 @@ module Mint end end + class StylePool + @pool = NamePool(Ast::Style, Nil).new + @cache = {} of Ast::Style => String + + def initialize(@optimize : Bool = false) + end + + def of(subject : Ast::Style, id : String? = nil) + if @optimize + @pool.of(subject, nil) + else + @cache[subject] ||= begin + if id + "#{id}_#{subject.name.value.gsub('-', '_')}" + else + subject.name.value.gsub('-', '_') + end + end + end + end + end + class PropertyValue property default : String? property variable : String? @@ -127,11 +149,11 @@ module Mint getter selectors, property_pool, name_pool, style_pool, variables, ifs getter cases - def initialize(@css_prefix : String? = nil) + def initialize(@css_prefix : String? = nil, @optimize : Bool = false) # Three name pools so there would be no clashes, # which also good for optimizations. + @style_pool = StylePool.new(optimize: @optimize) @property_pool = NamePool(String, String).new - @style_pool = NamePool(Ast::Node, Nil).new @name_pool = NamePool(String, Nil).new # This is the main data structure: @@ -192,14 +214,14 @@ module Mint false end - def prefixed_class_name(node) - @css_prefix.to_s + style_pool.of(node, nil) + def prefixed_class_name(node : Ast::Style, id : String? = nil) + @css_prefix.to_s + style_pool.of(node, id) end # The main entry point for processing a "style" tag. - def process(node : Ast::Style) + def process(node : Ast::Style, id : String? = nil) selectors = - [".#{prefixed_class_name(node)}"] + [".#{prefixed_class_name(node, id)}"] process(node.body, nil, nil, selectors, %w[], node) end