Skip to content

Commit 84802f6

Browse files
committed
Added jsx functions to generator module
1 parent acc2bf7 commit 84802f6

File tree

4 files changed

+195
-22
lines changed

4 files changed

+195
-22
lines changed

README.md

+22-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ Defines structs that represent the JavaScript AST nodes from the ESTree spec.
44

55
[ESTree Spec](https://github.com/estree/estree)
66

7+
[JSX AST Spec](https://github.com/facebook/jsx)
8+
79
Also includes a JavaScript AST to JavaScript code generator.
810

911
```elixir
@@ -16,5 +18,23 @@ ast = Builder.array_expression([
1618
])
1719

1820
Generator.generate(ast)
19-
#"[1, a]"
20-
```
21+
# "[1, a]"
22+
23+
#jsx ast and generation
24+
ast = Builder.jsx_element(
25+
Builder.jsx_opening_element(
26+
Builder.jsx_identifier(
27+
"Test"
28+
)
29+
),
30+
[],
31+
Builder.jsx_closing_element(
32+
Builder.jsx_identifier(
33+
"Test"
34+
)
35+
)
36+
)
37+
38+
Generator.generate(ast)
39+
# "<Test></Test>"
40+
```

lib/es_tree/tools/builder.ex

+2-2
Original file line numberDiff line numberDiff line change
@@ -831,7 +831,7 @@ defmodule ESTree.Tools.Builder do
831831
boolean,
832832
ESTree.SourceLocation.t | nil
833833
) :: ESTree.JSXOpeningElement.t
834-
def jsx_opening_element(name, attributes, selfClosing \\ false, loc \\ nil) do
834+
def jsx_opening_element(name, attributes \\ [], selfClosing \\ false, loc \\ nil) do
835835
%ESTree.JSXOpeningElement{
836836
name: name, attributes: attributes, selfClosing: selfClosing, loc: loc
837837
}
@@ -876,7 +876,7 @@ defmodule ESTree.Tools.Builder do
876876
ESTree.JSXClosingElement.t | nil,
877877
ESTree.SourceLocation.t | nil
878878
) :: ESTree.JSXElement.t
879-
def jsx_element(openingElement, children, closingElement \\ nil, loc \\ nil) do
879+
def jsx_element(openingElement, children \\ [], closingElement \\ nil, loc \\ nil) do
880880
%ESTree.JSXElement{
881881
openingElement: openingElement, children: children, closingElement: closingElement, loc: loc
882882
}

lib/es_tree/tools/generator.ex

+20-18
Original file line numberDiff line numberDiff line change
@@ -701,44 +701,46 @@ defmodule ESTree.Tools.Generator do
701701
"await #{generate(argument)}"
702702
end
703703

704-
def do_generate(%ESTree.JSXIdentifier{}, level) do
705-
""
704+
def do_generate(%ESTree.JSXIdentifier{ name: name }, level) do
705+
"#{name}"
706706
end
707707

708-
def do_generate(%ESTree.JSXMemberExpression{}, level) do
709-
""
708+
def do_generate(%ESTree.JSXMemberExpression{ object: object, property: property }, level) do
709+
"#{ generate(object) }.#{ generate(property) }"
710710
end
711711

712-
def do_generate(%ESTree.JSXNamespacedName{}, level) do
713-
""
712+
def do_generate(%ESTree.JSXNamespacedName{ namespace: namespace, name: name }, level) do
713+
"#{ generate(namespace) }:#{ generate(name) }"
714714
end
715715

716716
def do_generate(%ESTree.JSXEmptyExpression{}, level) do
717717
""
718718
end
719719

720-
def do_generate(%ESTree.JSXExpressionContainer{}, level) do
721-
""
720+
def do_generate(%ESTree.JSXExpressionContainer{ expression: expression }, level) do
721+
"{#{ generate(expression) }}"
722722
end
723723

724-
def do_generate(%ESTree.JSXOpeningElement{}, level) do
725-
""
724+
def do_generate(%ESTree.JSXOpeningElement{ name: name, attributes: attributes, selfClosing: selfClosing }, level) do
725+
selfClosing = if selfClosing, do: "/", else: ""
726+
727+
"<#{generate(name)} #{ Enum.map(attributes, &generate(&1)) |> Enum.join(" ") }#{selfClosing}>"
726728
end
727729

728-
def do_generate(%ESTree.JSXClosingElement{}, level) do
729-
""
730+
def do_generate(%ESTree.JSXClosingElement{ name: name }, level) do
731+
"</#{ generate(name) }>"
730732
end
731733

732-
def do_generate(%ESTree.JSXAttribute{}, level) do
733-
""
734+
def do_generate(%ESTree.JSXAttribute{ name: name, value: value }, level) do
735+
"#{ generate(name) }=#{ generate(value) }"
734736
end
735737

736-
def do_generate(%ESTree.JSXSpreadAttribute{}, level) do
737-
""
738+
def do_generate(%ESTree.JSXSpreadAttribute{ argument: argument }, level) do
739+
"{...#{ generate(argument) }}"
738740
end
739741

740-
def do_generate(%ESTree.JSXElement{}, level) do
741-
""
742+
def do_generate(%ESTree.JSXElement{ openingElement: openingElement, children: children, closingElement: closingElement }, level) do
743+
"#{ generate(openingElement) } #{ Enum.map(children, &generate(&1, level + 1)) |> Enum.join(" ") } #{ generate(closingElement) }"
742744
end
743745

744746
defp convert_string_characters(str) do

test/tools/generator/jsx_test.exs

+151
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
defmodule ESTree.Tools.Generator.JSX.Test do
2+
use ShouldI
3+
alias ESTree.Tools.Builder
4+
alias ESTree.Tools.Generator
5+
6+
7+
should "handle no closing tag" do
8+
ast = Builder.jsx_element(
9+
Builder.jsx_opening_element(
10+
Builder.jsx_identifier(
11+
"Test"
12+
),
13+
[],
14+
true
15+
)
16+
)
17+
18+
assert Generator.generate(ast) == "<Test/>"
19+
end
20+
21+
should "handle closing tag" do
22+
ast = Builder.jsx_element(
23+
Builder.jsx_opening_element(
24+
Builder.jsx_identifier(
25+
"Test"
26+
)
27+
),
28+
[],
29+
Builder.jsx_closing_element(
30+
Builder.jsx_identifier(
31+
"Test"
32+
)
33+
)
34+
)
35+
36+
assert Generator.generate(ast) == "<Test></Test>"
37+
end
38+
39+
40+
should "handle element with attributes" do
41+
ast = Builder.jsx_element(
42+
Builder.jsx_opening_element(
43+
Builder.jsx_identifier("Test"),
44+
[
45+
Builder.jsx_attribute(
46+
Builder.jsx_identifier("className"),
47+
Builder.literal("test")
48+
),
49+
Builder.jsx_attribute(
50+
Builder.jsx_identifier("name"),
51+
Builder.jsx_expression_container(
52+
Builder.array_expression([
53+
Builder.literal(1)
54+
])
55+
)
56+
),
57+
Builder.jsx_spread_attribute(
58+
Builder.array_expression([
59+
Builder.literal(1)
60+
])
61+
)
62+
]
63+
),
64+
[],
65+
Builder.jsx_closing_element(
66+
Builder.jsx_identifier(
67+
"Test"
68+
)
69+
)
70+
)
71+
72+
assert Generator.generate(ast) == "<Test className='test' name={[1]} {...[1]}></Test>"
73+
end
74+
75+
76+
should "handle element with elements inside" do
77+
ast = Builder.jsx_element(
78+
Builder.jsx_opening_element(
79+
Builder.jsx_identifier("Test")
80+
),
81+
[
82+
Builder.jsx_element(
83+
Builder.jsx_opening_element(
84+
Builder.jsx_identifier("div"), [], true
85+
)
86+
)
87+
],
88+
Builder.jsx_closing_element(
89+
Builder.jsx_identifier(
90+
"Test"
91+
)
92+
)
93+
)
94+
95+
assert Generator.generate(ast) == "<Test><div/></Test>"
96+
end
97+
98+
99+
should "handle namespaced names" do
100+
ast = Builder.jsx_element(
101+
Builder.jsx_opening_element(
102+
Builder.jsx_namespaced_name(
103+
Builder.jsx_identifier("Test"),
104+
Builder.jsx_identifier("xml")
105+
)
106+
),
107+
[
108+
Builder.jsx_element(
109+
Builder.jsx_opening_element(
110+
Builder.jsx_identifier("div"), [], true
111+
)
112+
)
113+
],
114+
Builder.jsx_closing_element(
115+
Builder.jsx_namespaced_name(
116+
Builder.jsx_identifier("Test"),
117+
Builder.jsx_identifier("xml")
118+
)
119+
)
120+
)
121+
122+
assert Generator.generate(ast) == "<Test:xml><div/></Test:xml>"
123+
end
124+
125+
126+
should "handle member names" do
127+
ast = Builder.jsx_element(
128+
Builder.jsx_opening_element(
129+
Builder.jsx_member_expression(
130+
Builder.jsx_identifier("Test"),
131+
Builder.jsx_identifier("xml")
132+
)
133+
),
134+
[
135+
Builder.jsx_element(
136+
Builder.jsx_opening_element(
137+
Builder.jsx_identifier("div"), [], true
138+
)
139+
)
140+
],
141+
Builder.jsx_closing_element(
142+
Builder.jsx_member_expression(
143+
Builder.jsx_identifier("Test"),
144+
Builder.jsx_identifier("xml")
145+
)
146+
)
147+
)
148+
149+
assert Generator.generate(ast) == "<Test.xml><div/></Test.xml>"
150+
end
151+
end

0 commit comments

Comments
 (0)