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/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 { 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 43312f6cb..4a9925a08 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 { @@ -13,24 +13,30 @@ component Main { } -------------------------------------------------------------------------------- class A extends _C { + $a() { + const _ = { + [`--a-a`]: this.a + `px 0px` + }; + + return _; + } + get a() { - return 10 + return 10; } render() { return _h("div", { className: `a`, - style: { - [`--a-a`]: this.a + `px 0px` - } - }) + 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 new file mode 100644 index 000000000..2ff155219 --- /dev/null +++ b/spec/compilers/css_media @@ -0,0 +1,55 @@ +component Main { + style test { + div { + color: #{color}; + } + + @media (screen) { + color: #{color}; + } + } + + get color : String { + "blue" + } + + fun render : Html { + + } +} +-------------------------------------------------------------------------------- +class A extends _C { + $a() { + const _ = { + [`--a-a`]: this.a, + [`--b-a`]: this.a + }; + + return _; + } + + get a() { + return `blue`; + } + + render() { + return _h("div", { + className: `a`, + style: _style([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_media_with_if b/spec/compilers/css_media_with_if new file mode 100644 index 000000000..e3a94053a --- /dev/null +++ b/spec/compilers/css_media_with_if @@ -0,0 +1,48 @@ +component Main { + style test { + color: yellow; + + @media (max-width: 300px) { + if (true) { + color: red; + } + } + } + + fun render : Html { + + } +} +-------------------------------------------------------------------------------- +class A extends _C { + $a() { + const _ = {}; + + (true ? Object.assign(_, { + [`--a-a`]: `red` + }) : null); + + 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_selector b/spec/compilers/css_selector index 89cf40485..5302ec8a5 100644 --- a/spec/compilers/css_selector +++ b/spec/compilers/css_selector @@ -1,8 +1,9 @@ component Main { style test { - & div { - color: {color}; + div { + color: #{color}; } + &:focus { color: red; } @@ -18,27 +19,29 @@ component Main { } -------------------------------------------------------------------------------- class A extends _C { + $a() { + const _ = { + [`--a-a`]: this.a + }; + + return _; + } + get a() { - return `blue` + return `blue`; } render() { return _h("div", { className: `a`, - style: { - [`--a-a`]: this.a - } - }) + style: _style([this.$a()]) + }); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; _insertStyles(` -.a { - -} - .a div { color: var(--a-a); } @@ -46,4 +49,4 @@ _insertStyles(` .a:focus { color: red; } -`) +`); diff --git a/spec/compilers/css_with_arguments b/spec/compilers/css_with_arguments new file mode 100644 index 000000000..39d38fcb4 --- /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/compilers/css_with_case b/spec/compilers/css_with_case new file mode 100644 index 000000000..fa9eb02c4 --- /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`)) { + 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/spec/compilers/css_with_if b/spec/compilers/css_with_if new file mode 100644 index 000000000..8b16632da --- /dev/null +++ b/spec/compilers/css_with_if @@ -0,0 +1,44 @@ +component Main { + style test { + color: yellow; + + if (true) { + color: red; + } else { + color: blue; + } + } + + fun render : Html { + + } +} +-------------------------------------------------------------------------------- +class A extends _C { + $a() { + const _ = {}; + + (true ? Object.assign(_, { + [`--a-a`]: `red` + }) : 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/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 new file mode 100644 index 000000000..7eaaad503 --- /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/compilers/html_with_pseudos b/spec/compilers/html_with_pseudos index 12d4af0ff..2c486a9cc 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"}; + div { + font-family: #{"Hello"}; color: blue; } } @@ -25,7 +25,7 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { constructor(props) { - super(props) + super(props); this._d({ b: [ @@ -36,22 +36,28 @@ class A extends _C { null, `blue` ] - }) + }); + } + + $a() { + const _ = { + [`--a-a`]: this.a, + [`--b-a`]: this.b, + [`--c-a`]: `Hello` + }; + + return _; } render() { return _h("div", { className: `a`, - style: { - [`--a-a`]: this.a, - [`--a-b`]: this.b, - [`--a-c`]: `Hello` - } - }) + style: _style([this.$a()]) + }); } -} +}; -A.displayName = "Main" +A.displayName = "Main"; _insertStyles(` .a { @@ -60,12 +66,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/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 d0b0a5ffb..6ddb4069e 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 { - background: {background}; - color: red; + -webkit-tap-highlight-color: rgba(0,0,0,0); + -webkit-touch-callout: none; + + border-color: #{background}; + background: #{background}; + border: #{background}; + color: #{color}; } fun render : Html { @@ -14,31 +20,48 @@ component Main { -------------------------------------------------------------------------------- class A extends _C { constructor(props) { - super(props) + super(props); this._d({ a: [ null, `blue` + ], + b: [ + null, + `yellow` ] - }) + }); + } + + $a() { + const _ = { + [`--a-a`]: this.a, + [`--a-b`]: this.a, + [`--a-c`]: this.a, + [`--a-d`]: this.b + }; + + return _; } render() { return _h("div", { className: `a`, - style: { - [`--a-a`]: this.a - } - }) + style: _style([this.$a()]) + }); } -} +}; -A.displayName = "Main" +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 e86289ab5..7e5352949 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; } @@ -18,35 +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 _; } get b() { - return + return; } render() { return _h("div", { className: `a`, - style: _style([{ - [`--a-a`]: this.a - }, this.b]) - }) + 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/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_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_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/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/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/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/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/spec/parsers/css_definition_spec.cr b/spec/parsers/css_definition_spec.cr index 15fb52598..877afb0e9 100644 --- a/spec/parsers/css_definition_spec.cr +++ b/spec/parsers/css_definition_spec.cr @@ -7,16 +7,15 @@ 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 - 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/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/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/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/style_builder_spec.cr b/spec/style_builder_spec.cr new file mode 100644 index 000000000..b149c74ec --- /dev/null +++ b/spec/style_builder_spec.cr @@ -0,0 +1,66 @@ +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; + + if (true) { + color: red; + } + } + } + + @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/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 new file mode 100644 index 000000000..6cf93a4e0 --- /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/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/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/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/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/html_element.cr b/src/ast/html_element.cr index c0e365181..9cd896ba7 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), @children : Array(HtmlContent), @comments : Array(Comment), - @style : Variable | Nil, + @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/ast/if.cr b/src/ast/if.cr index 41aaaaa83..b52d45593 100644 --- a/src/ast/if.cr +++ b/src/ast/if.cr @@ -1,17 +1,21 @@ 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(CssDefinition), Array(CssDefinition)) | + Tuple(Array(CssDefinition), Nil) | + Tuple(Array(CssDefinition), 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/ast/style.cr b/src/ast/style.cr index 5ce5ff66c..6044a2f35 100644 --- a/src/ast/style.cr +++ b/src/ast/style.cr @@ -1,12 +1,10 @@ module Mint class Ast class Style < Node - getter name, definitions, selectors, medias, comments + getter name, body, arguments - def initialize(@definitions : Array(CssDefinition), - @selectors : Array(CssSelector), - @comments : Array(Comment), - @medias : Array(CssMedia), + def initialize(@arguments : Array(Argument), + @body : Array(Node), @name : Variable, @input : Data, @from : Int32, diff --git a/src/compiler.cr b/src/compiler.cr index 901dc1625..21f61ad4d 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 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/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..75cab3c47 100644 --- a/src/compilers/case_branch.cr +++ b/src/compilers/case_branch.cr @@ -1,16 +1,34 @@ 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 = + if block + _compile item, block + else + "{}" + end + when Ast::Node + js.return(compile(item)) + else + "" + end if match = node.match case match @@ -24,7 +42,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 +50,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/compilers/component.cr b/src/compilers/component.cr index 31949e367..644ec189a 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([ @@ -81,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/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..bf647ead5 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 @@ -22,23 +40,14 @@ module Mint .map { |attribute| resolve(attribute) } .reduce({} of String => String) { |memo, item| memo.merge(item) } - component = - html_elements[node]? + style_nodes = + node.styles.map { |item| lookups[item] } class_name = - if node.style && component - js.style_of(lookups[node]) - end - - class_names = - if class_name - medias - .values - .map(&.keys) - .flatten - .select(&.starts_with?(class_name + "_")) - .push(class_name) - .join(" ") + if style_nodes.any? + style_nodes.map do |style_node| + style_builder.style_pool.of(style_node, nil) + end.join(" ") end class_name_attribute = @@ -52,36 +61,36 @@ 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? - end - custom_styles = node .attributes .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 + + node.styles.each do |item| + if style_builder.any?(lookups[item]) + arguments = + compile item.arguments + + styles << js.call("this.$#{class_name}", arguments) + end + end + + 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..9d6ba17db 100644 --- a/src/compilers/if.cr +++ b/src/compilers/if.cr @@ -1,14 +1,56 @@ 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 + truthy_item, falsy_item = + node.branches + truthy = - compile node.truthy + case item = truthy_item + when Array(Ast::CssDefinition) + _compile item, block: block + else + compile item + end falsy = - compile node.falsy + 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/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/compilers/style.cr b/src/compilers/style.cr index 60ba20859..34abf374a 100644 --- a/src/compilers/style.cr +++ b/src/compilers/style.cr @@ -15,20 +15,7 @@ module Mint end def _compile(node : Ast::Style, component : Ast::Component) : Nil - prefix = - js.style_of(node) - - compile prefix, prefix, node.definitions - - node.medias.each_with_index do |item, index| - compile prefix + "_media_#{index}", prefix, item.definitions, item.content - end - - node.selectors.each do |item| - item.selectors.each do |selector| - compile prefix + selector, prefix, item.definitions - 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..e748e9d68 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 = @@ -205,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/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/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/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/html_element.cr b/src/formatters/html_element.cr index 63f6e2c0b..2cbecbc40 100644 --- a/src/formatters/html_element.cr +++ b/src/formatters/html_element.cr @@ -4,12 +4,9 @@ module Mint tag = format node.tag - style = - format node.style - prefix = - if style - "#{tag}::#{style}" + 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/formatters/if.cr b/src/formatters/if.cr index dbfb0ea47..1cbd7b50f 100644 --- a/src/formatters/if.cr +++ b/src/formatters/if.cr @@ -4,19 +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::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 + 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 + " else " + format(falsy_item) else body = - list [node.falsy] + node.falsy_head_comments + node.falsy_tail_comments + case falsy_item + 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 + end - "{\n#{indent(body)}\n}" + " else {\n#{indent(body)}\n}" if body end condition = @@ -29,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 3e22a0867..f07a64844 100644 --- a/src/formatters/style.cr +++ b/src/formatters/style.cr @@ -1,16 +1,27 @@ 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 + + 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} {\n#{indent(body)}\n}" + "style #{name} #{arguments}{\n#{indent(body)}\n}" 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 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/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/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/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/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/css_definition.cr b/src/parsers/css_definition.cr index f52e1b9c1..da6542b91 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,12 +11,14 @@ module Mint chars "a-zA-Z-" end - char ':', CssDefinitionExpectedColon + 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 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 diff --git a/src/parsers/css_media.cr b/src/parsers/css_media.cr index dbe77c99e..8b0c82040 100644 --- a/src/parsers/css_media.cr +++ b/src/parsers/css_media.cr @@ -12,35 +12,22 @@ 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, 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/html_element.cr b/src/parsers/html_element.cr index 452aff9b2..85e8f0fa7 100644 --- a/src/parsers/html_element.cr +++ b/src/parsers/html_element.cr @@ -15,8 +15,14 @@ module Mint skip unless tag - if keyword "::" - style = variable_with_dashes! HtmlElementExpectedStyle + styles = [] of Ast::HtmlStyle + + if keyword_ahead "::" + styles.concat(many(parse_whitespace: false) do + html_style + end.compact) + + raise HtmlElementExpectedStyle if styles.empty? end ref = start do @@ -37,7 +43,7 @@ module Mint from: start_position, children: children, comments: comments, - style: style, + styles: styles, to: position, input: data, tag: tag, 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/parsers/if.cr b/src/parsers/if.cr index 023a23a8b..7a2c0ad30 100644 --- a/src/parsers/if.cr +++ b/src/parsers/if.cr @@ -11,7 +11,7 @@ module Mint syntax_error IfExpectedCondition syntax_error IfExpectedElse - def if_expression : Ast::If | Nil + def if_expression(for_css = false) : Ast::If | Nil start do |start_position| skip unless keyword "if" @@ -27,24 +27,36 @@ module Mint opening_bracket: IfExpectedTruthyOpeningBracket, closing_bracket: IfExpectedTruthyClosingBracket ) do - expression! IfExpectedTruthyExpression + if for_css + many { css_definition }.compact + else + expression! IfExpectedTruthyExpression + end end - whitespace - keyword! "else", IfExpectedElse + falsy_head_comments = [] of Ast::Comment + falsy_tail_comments = [] of Ast::Comment + falsy = nil + 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 - expression! IfExpectedFalsyExpression - end + if !for_css || keyword_ahead "else" + keyword! "else", IfExpectedElse + whitespace + + 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 { css_definition }.compact + else + expression! IfExpectedFalsyExpression + end + end + end end Ast::If.new( @@ -53,8 +65,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 97cafb263..9746e2fde 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,44 +10,58 @@ 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, 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 end Ast::Style.new( - definitions: definitions, - selectors: selectors, from: start_position, - comments: comments, - medias: medias, + arguments: arguments, to: position, input: data, + body: body, name: name) end end + + def css_body + many { + comment || + case_expression(for_css: true) || + if_expression(for_css: true) || + css_media || + 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 diff --git a/src/style_builder.cr b/src/style_builder.cr new file mode 100644 index 000000000..f09746e7a --- /dev/null +++ b/src/style_builder.cr @@ -0,0 +1,285 @@ +module Mint + # 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 + + @cache : Hash(Tuple(B, T), String) = {} of Tuple(B, T) => String + @current : Hash(B, String) = {} of B => String + + def of(subject : T, base : B) + @cache[{base, subject}] ||= begin + @current[base] = (@current[base]? || INITIAL).succ + end + 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, cases, 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_conditions = + ifs + .select(&.first.==(node)) + .merge(cases.select(&.first.==(node))) + .map do |(_, selector), conditions| + statements = + conditions.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) + + case item + when Ast::If, Ast::Case + compile item, proc + else + "" + end + end + + js.statements(statements) + end + + arguments = + compile node.arguments + + js.function("$" + style_pool.of(node, nil), arguments, + js.statements([[ + js.const("_", static), + compiled_conditions, + js.return("_"), + ]].flatten.reject(&.empty?))) + end + + def compile_branch(items : Array(Ast::CssDefinition), selector : StyleBuilder::Selector) + compiled = + items + .each_with_object({} of String => String) do |definition, memo| + variable = + variable_name definition.name, selector + + selector[definition.name] ||= PropertyValue.new + selector[definition.name].variable = 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. + class StyleBuilder + class Selector < Hash(String, PropertyValue) + getter id : String = Random::Secure.hex + end + + getter selectors, property_pool, name_pool, style_pool, variables, ifs + getter cases + + def initialize + # Three name pools so there would be no clashes, + # which also good for optimizations. + @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: + # + # 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)) + @cases = {} of Tuple(Ast::Node, Selector) => Array(Ast::Case) + @ifs = {} of Tuple(Ast::Node, Selector) => Array(Ast::If) + end + + # Compiles the processed data into a CSS style sheet. + def compile + output = {} of Array(String) => Array(String) + + selectors + .reject { |_, v| v.empty? } + .each do |(medias, rules), properties| + selector = + rules.join(",\n") + + body = + properties + .map { |key, value| "#{key}: #{value.to_s};" } + .join("\n") + + output[medias] ||= [] of String + output[medias] << "#{selector} {\n#{body.indent}\n}" + end + + 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 compile_style(node : Ast::Style, compiler : Compiler) + VariableCompiler + .new(self, compiler) + .compile(node) + end + + def any?(node : Ast::Node) + variables[node]? || + ifs.any?(&.first.first.==(node)) || + cases.any?(&.first.first.==(node)) + end + + def any?(node : Nil) + false + end + + # The main entry point for processing a "style" tag. + def process(node : Ast::Style) + selectors = + ["." + style_pool.of(node, nil)] + + process(node.body, selectors, [] of String, node) + end + + # Processes a 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 + + process(node.body, selectors, media, style_node) + end + + # 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 + + body.each do |item| + case item + when Ast::CssDefinition + if item.value.any?(Ast::CssInterpolation) + # Get the name of the variable + variable = + variable_name(item.name, selector) + + 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] ||= PropertyValue.new + selector[item.name].default = item.value.join("") + end + when Ast::CssSelector + 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 + end + end + end + + def variable_name(name, selector) + # Get the unique ID of the selector + block_id = + name_pool.of(selector.id, nil) + + # Get the unique ID of the property + variable_id = + property_pool.of(name, selector.id) + + "--#{block_id}-#{variable_id}" + end + end +end diff --git a/src/type_checker.cr b/src/type_checker.cr index fb2d38d3e..8d42f2a3f 100644 --- a/src/type_checker.cr +++ b/src/type_checker.cr @@ -29,7 +29,7 @@ module Mint property checking : Bool = true - delegate types, variables, html_elements, ast, lookups, cache, 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 000337063..3398b130f 100644 --- a/src/type_checkers/artifacts.cr +++ b/src/type_checkers/artifacts.cr @@ -1,15 +1,11 @@ module Mint class TypeChecker class Artifacts - getter types, variables, html_elements, dynamic_styles, styles - getter ast, medias, lookups, cache, checked, record_field_lookup + getter ast, lookups, cache, checked, record_field_lookup + getter types, variables 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, 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 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/html_element.cr b/src/type_checkers/html_element.cr index 83216736e..20bcfc0a6 100644 --- a/src/type_checkers/html_element.cr +++ b/src/type_checkers/html_element.cr @@ -2,29 +2,14 @@ module Mint class TypeChecker type_error HtmlElementReferenceOutsideOfComponent type_error HtmlElementStyleOutsideOfComponent - type_error HtmlElementNotFoundStyle def check(node : Ast::HtmlElement) : Checkable - style = - node.style - - if style + if node.styles.any? raise HtmlElementStyleOutsideOfComponent, { - "node" => style, + "node" => node, } unless component? - style_node = - component.styles.find(&.name.value.==(style.value)) - - raise HtmlElementNotFoundStyle, { - "style" => style.value, - "node" => style, - } unless style_node - - resolve style_node - - lookups[node] = style_node - 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 diff --git a/src/type_checkers/if.cr b/src/type_checkers/if.cr index aafd5dfca..964c9f399 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 + falsy_item.try { |data| resolve data } - truthy + NEVER + end end end end 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 a7f19f605..f8015107e 100644 --- a/src/type_checkers/style.cr +++ b/src/type_checkers/style.cr @@ -1,11 +1,12 @@ module Mint class TypeChecker def check(node : Ast::Style) : Checkable - resolve node.definitions - resolve node.selectors - resolve node.medias + scope node do + resolve node.arguments + resolve node.body - NEVER + NEVER + end end end end