Skip to content

Commit

Permalink
Merge pull request #415 from eyakubovich/ey/support-tsx-jsx-element
Browse files Browse the repository at this point in the history
Support JSX elements in TSX
  • Loading branch information
hendrikvanantwerpen authored Mar 15, 2024
2 parents cebac48 + 1e12072 commit a8c7109
Show file tree
Hide file tree
Showing 7 changed files with 311 additions and 14 deletions.
23 changes: 13 additions & 10 deletions languages/tree-sitter-stack-graphs-typescript/rust/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,18 @@ use tree_sitter_stack_graphs::ci::Tester;
use tree_sitter_stack_graphs::NoCancellation;

fn main() -> anyhow::Result<()> {
let lc = match tree_sitter_stack_graphs_typescript::try_language_configuration_typescript(
&NoCancellation,
) {
Ok(lc) => lc,
Err(err) => {
eprintln!("{}", err.display_pretty());
return Err(anyhow!("Language configuration error"));
}
};
let lc_factories = [
tree_sitter_stack_graphs_typescript::try_language_configuration_typescript,
tree_sitter_stack_graphs_typescript::try_language_configuration_tsx,
];

let lcs = lc_factories
.iter()
.map(|lc_factory| lc_factory(&NoCancellation))
.collect::<Result<Vec<_>, _>>()
.inspect_err(|err| eprintln!("{}", err.display_pretty()))
.map_err(|_| anyhow!("Language configuration error"))?;

let test_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test");
Tester::new(vec![lc], vec![test_path]).run()
Tester::new(lcs, vec![test_path]).run()
}
176 changes: 172 additions & 4 deletions languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg
Original file line number Diff line number Diff line change
Expand Up @@ -2667,6 +2667,13 @@ if none @is_async {
(true)
; #dialect typescript
(type_assertion)
; #end
; #dialect tsx
(jsx_element)
(jsx_self_closing_element)
(jsx_opening_element)
(jsx_closing_element)
(jsx_expression)
; #end
(unary_expression)
(undefined)
Expand Down Expand Up @@ -2805,6 +2812,15 @@ if none @is_async {
(decorator (call_expression function:(member_expression object:(identifier)@name)))
(decorator (call_expression function:(member_expression object:(member_expression object:(identifier)@name))))
(decorator (call_expression function:(member_expression object:(member_expression object:(member_expression object:(identifier)@name)))))
; #dialect tsx
(nested_identifier (identifier)@name)
(nested_identifier (nested_identifier)@name)
(nested_type_identifier module:(nested_identifier)@name)
(internal_module name:(_)@name)
(jsx_opening_element name: (_)@name)
(jsx_closing_element name: (_)@name)
(jsx_self_closing_element name: (_)@name)
; #end
] {
if none @is_def {
node @name.cotype
Expand Down Expand Up @@ -2852,6 +2868,12 @@ if none @is_def {
(decorator (call_expression function:(member_expression object:(identifier)@name)))
(decorator (call_expression function:(member_expression object:(member_expression object:(identifier)@name))))
(decorator (call_expression function:(member_expression object:(member_expression object:(member_expression object:(identifier)@name)))))
; #dialect tsx
(nested_identifier (identifier)@name (identifier)) ; to pick up foo in JSX: <foo.bar.baz />
(jsx_opening_element name: (identifier)@name)
(jsx_closing_element name: (identifier)@name)
(jsx_self_closing_element name: (identifier)@name)
; #end
] {
if none @is_def {
node @name.expr_ref
Expand Down Expand Up @@ -3524,10 +3546,22 @@ if none @is_async {
; (member_expression (identifier) (property_identifier))
; (subscript_expression (identifier) (string))

(member_expression
object: (_)@object
property: (_)@prop
)@member_expr {
[
(member_expression
object: (_)@object
property: (_)@prop
)@member_expr
; #dialect tsx
(nested_identifier
(nested_identifier)@object
(identifier)@prop
)@member_expr
(nested_identifier
(identifier)@object
(identifier)@prop
)@member_expr
; #end
] {
node @member_expr.member
node @prop.expr_ref
node @prop.expr_ref__typeof
Expand Down Expand Up @@ -6160,3 +6194,137 @@ if none @is_acc {
attr (@async.async_type__promise_ref__ns) push_symbol = "%T"
edge @async.async_type__promise_ref__ns -> @async.lexical_scope
}

;
; # ##### # #
; # # # # #
; # # # #
; # ##### #
; # # # # #
; # # # # # #
; ##### ##### # #
;
; ;;;;;;;;;;;;;;;;;;;;
; #dialect tsx
; ;;;;;;;;;;;;;;;;;;;;

(jsx_element
open_tag:(_)@open_tag
close_tag:(_)@close_tag)@jsx_element {

edge @open_tag.lexical_scope -> @jsx_element.lexical_scope
edge @close_tag.lexical_scope -> @jsx_element.lexical_scope
}

(jsx_element
[
(jsx_text)
(jsx_element)
(jsx_fragment)
(jsx_self_closing_element)
(jsx_expression)
]@child
)@parent {
edge @child.lexical_scope -> @parent.lexical_scope
}

(jsx_fragment)@fragment {
node @fragment.lexical_scope
node @fragment.value
node @fragment.type
}

(jsx_fragment
[
(jsx_text)
(jsx_element)
(jsx_fragment)
(jsx_self_closing_element)
(jsx_expression)
]@child
) @parent {
edge @child.lexical_scope -> @parent.lexical_scope
}

(jsx_text)@jsx_text {
node @jsx_text.lexical_scope
}

[
(jsx_opening_element name: (_)@name)@element
(jsx_closing_element name: (_)@name)@element
(jsx_self_closing_element name: (_)@name)@element
] {
edge @name.lexical_scope -> @element.lexical_scope
}

(jsx_opening_element
name:(_)@element_name
attribute:(_)@attr
) {
edge @attr.lexical_scope -> @element_name.lexical_scope
}

(jsx_attribute (_) . (_)?@attr_value)@jsx_attribute {
node @jsx_attribute.lexical_scope

if some @attr_value {
edge @attr_value.lexical_scope -> @jsx_attribute.lexical_scope
}
}

(jsx_namespace_name (_) @lhs (_) @rhs)@name {
node @name.lexical_scope

edge @lhs.lexical_scope -> @name.lexical_scope
edge @rhs.lexical_scope -> @name.lexical_scope
}

(jsx_self_closing_element
name:(_)@element_name)@element {
edge @element_name.lexical_scope -> @element.lexical_scope
}

(jsx_self_closing_element
name:(_)@element_name
attribute:(_)@attr
) {
edge @attr.lexical_scope -> @element_name.lexical_scope
}

(jsx_expression (_)@child)@expr {
edge @child.lexical_scope -> @expr.lexical_scope
}

(jsx_closing_element
name:(_)@element_name)@element
{
edge @element_name.lexical_scope -> @element.lexical_scope
}

[
(jsx_opening_element
name:(identifier)@element_name)
(jsx_self_closing_element
name:(identifier)@element_name)
(jsx_closing_element
name:(identifier)@element_name)
] {
scan (source-text @element_name) {
; standard HTML elements
"^(a|abbr|acronym|address|applet|area|article|aside|audio|b|base|basefont|bdi|bdo|big|blockquote|body|br|button|canvas|caption|center|cite|code|col|colgroup|data|datalist|dd|del|details|dfn|dialog|dir|div|dl|dt|em|embed|fieldset|figcaption|figure|font|footer|form|frame|frameset|h1|h2|h3|h4|h5|h6|head|header|hgroup|hr|html|i|iframe|input|ins|kbd|label|legend|li|link|main|map|mark|menu|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|picture|pre|progress|q|rp|rt|ruby|s|samp|script|search|section|select|small|source|span|strike|strong|style|sub|summary|sup|svg|table|tbody|td|template|textarea|tfoot|th|thead|time|title|tr|track|tt|u|ul|var|video|wbr)$" {
; do nothing!
}

; everything else
"^.+$" {
node element_name_pop
attr (element_name_pop) node_reference = @element_name
edge element_name_pop -> @element_name.lexical_scope
}
}
}

; ;;;;;;;;;;;;;;;;;;;;
; #end
; ;;;;;;;;;;;;;;;;;;;;
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// The core of JSX tests here verify the behavior of the following node types:
// jsx_element
// jsx_identifier
// jsx_attribute
// jsx_expression
// jsx_opening_element
// jsx_closing_element
// There is no real way to avoid testing all of these at once,
// and so we don't even try to.

let x = 1;

// Flow In

const el = <foo bar={x}>{x}</foo>;
// ^ defined: 11
// ^ defined: 11

const el2 = <x></x>
// ^ defined: 11
// ^ defined: 11

let y = 0;
let z = 2;

const el = <foo bar={y = 1}>
// ^ defined: 23
{z = 3}
// ^ defined: 24
</foo>;

/**/ y;
// ^ defined: 23

/**/ z;
// ^ defined: 24

/**/ x;
// ^ defined: 11
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
let x = 1;

// Flow Around

const el = <></>;

/**/ x;
// ^ defined: 1

// Children
(<foo><bar>{x}</bar></foo>);
// ^ defined: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
let x = 1;

// Flow Around
namespace noo.bar {
export let baz = 1;
}

const el = <noo.bar.baz></noo.bar.baz>;
// ^ defined: 4
// ^ defined: 4
// ^ defined: 5
// ^ defined: 4
// ^ defined: 4
// ^ defined: 5

/**/ x;
// ^ defined: 1

// Flow In

let foo = {
bar: {
baz: 1
}
};

const el2 = <foo.bar.baz />;
// ^ defined: 21
// ^ defined: 22
// ^ defined: 23
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// The core of JSX tests here verify the behavior of the following node types:
// jsx_element
// jsx_identifier
// jsx_attribute
// jsx_expression
// jsx_opening_element
// jsx_closing_element
// There is no real way to avoid testing all of these at once,
// and so we don't even try to.

let x = 1;

// Flow In

const el = <foo bar={x} />;
// ^ defined: 11

const el2 = <x />
// ^ defined: 11

// Flow Out

let y = 2;

const el = <foo bar={y = 1} />;
// ^ defined: 23

// Flow Across

const el = <foo bar={y = 1}
baz={y} />;
// ^ defined: 23

// Flow Around

/**/ x;
// ^ defined: 11
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
let x = 1;

// Flow Around

const el = <foo>bar</foo>;

/**/ x;
// ^ defined: 1

0 comments on commit a8c7109

Please sign in to comment.