Skip to content
This repository was archived by the owner on Sep 9, 2025. It is now read-only.

Commit c09f746

Browse files
author
Hendrik van Antwerpen
authored
Merge pull request #409 from eyakubovich/ey/support-tsx
Support for TSX
2 parents be64a09 + 805bffd commit c09f746

6 files changed

Lines changed: 167 additions & 13 deletions

File tree

languages/tree-sitter-stack-graphs-typescript/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,7 @@ tsconfig = "0.1.0"
4040
[dev-dependencies]
4141
anyhow = { version = "1.0" }
4242
tree-sitter-stack-graphs = { version = "0.8", path = "../../tree-sitter-stack-graphs", features = ["cli"] }
43+
44+
[build-dependencies]
45+
anyhow = { version = "1.0" }
46+
regex = "1.10.3"
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// -*- coding: utf-8 -*-
2+
// ------------------------------------------------------------------------------------------------
3+
// Copyright © 2024, stack-graphs authors.
4+
// Licensed under either of Apache License, Version 2.0, or MIT license, at your option.
5+
// Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details.
6+
// ------------------------------------------------------------------------------------------------
7+
8+
use std::path::Path;
9+
10+
use anyhow::{bail, Result};
11+
use regex::Regex;
12+
13+
const TSG_SOURCE: &str = "src/stack-graphs.tsg";
14+
const DIALECTS: [&str; 2] = ["typescript", "tsx"];
15+
16+
/// preprocess the input file, removing lines that are not for the selected dialect
17+
fn preprocess(
18+
input: impl std::io::Read,
19+
mut output: impl std::io::Write,
20+
dialect: &str,
21+
) -> Result<()> {
22+
// Matches: ; #dialect typescript
23+
let directive_start = Regex::new(r";\s*#dialect\s+(\w+)").unwrap();
24+
25+
// Matches: ; #end
26+
let directive_end = Regex::new(r";\s*#end").unwrap();
27+
28+
let no_code = Regex::new(r"^[\s;]+").unwrap();
29+
30+
let input = std::io::read_to_string(input)?;
31+
32+
// If the filter is None or Some(true), the lines are written to the output
33+
let mut filter: Option<bool> = None;
34+
35+
for (mut line_no, line) in input.lines().enumerate() {
36+
// Line numbers are one based
37+
line_no += 1;
38+
39+
if let Some(captures) = directive_start.captures(line) {
40+
if !no_code.is_match(line) {
41+
bail!("Line {line_no}: unexpected code before directive");
42+
}
43+
44+
let directive = captures.get(1).unwrap().as_str();
45+
if !DIALECTS.contains(&directive) {
46+
bail!("Line {line_no}: unknown dialect: {directive}");
47+
}
48+
49+
filter = Some(dialect == directive);
50+
} else if directive_end.is_match(line) {
51+
if !no_code.is_match(line) {
52+
bail!("Line {line_no}: unexpected code before directive end");
53+
}
54+
55+
if filter.is_none() {
56+
bail!("Line {line_no}: unmatched directive end");
57+
}
58+
59+
filter = None;
60+
} else if filter.unwrap_or(true) {
61+
output.write_all(line.as_bytes())?;
62+
}
63+
// a new line is always written so that removed lines are padded to preserve line numbers
64+
output.write(b"\n")?;
65+
}
66+
67+
Ok(())
68+
}
69+
70+
fn main() {
71+
let out_dir = std::env::var_os("OUT_DIR").expect("OUT_DIR is not set");
72+
73+
for dialect in DIALECTS {
74+
let input = std::fs::File::open(TSG_SOURCE).expect("Failed to open stack-graphs.tsg");
75+
76+
let out_filename = Path::new(&out_dir).join(format!("stack-graphs-{dialect}.tsg"));
77+
let output = std::fs::File::create(out_filename).expect("Failed to create output file");
78+
79+
preprocess(input, output, dialect).expect("Failed to preprocess stack-graphs.tsg");
80+
}
81+
82+
println!("cargo:rerun-if-changed={TSG_SOURCE}");
83+
println!("cargo:rerun-if-changed=build.rs");
84+
}

languages/tree-sitter-stack-graphs-typescript/rust/bin.rs

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,56 @@
66
// ------------------------------------------------------------------------------------------------
77

88
use anyhow::anyhow;
9-
use clap::Parser;
9+
use clap::{Parser, ValueEnum};
1010
use tree_sitter_stack_graphs::cli::database::default_user_database_path_for_crate;
1111
use tree_sitter_stack_graphs::cli::provided_languages::Subcommands;
12+
use tree_sitter_stack_graphs::loader::{LanguageConfiguration, LoadError};
1213
use tree_sitter_stack_graphs::NoCancellation;
1314

15+
/// Flag to select the dialect of the language
16+
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
17+
pub enum Dialect {
18+
Typescript,
19+
TSX,
20+
}
21+
1422
fn main() -> anyhow::Result<()> {
15-
let lc = match tree_sitter_stack_graphs_typescript::try_language_configuration(&NoCancellation)
16-
{
23+
let cli = Cli::parse();
24+
let lc = match language_configuration(cli.dialect) {
1725
Ok(lc) => lc,
1826
Err(err) => {
1927
eprintln!("{}", err.display_pretty());
2028
return Err(anyhow!("Language configuration error"));
2129
}
2230
};
23-
let cli = Cli::parse();
2431
let default_db_path = default_user_database_path_for_crate(env!("CARGO_PKG_NAME"))?;
2532
cli.subcommand.run(default_db_path, vec![lc])
2633
}
2734

35+
fn language_configuration<'a>(dialect: Dialect) -> Result<LanguageConfiguration, LoadError<'a>> {
36+
match dialect {
37+
Dialect::Typescript => {
38+
tree_sitter_stack_graphs_typescript::try_language_configuration_typescript(
39+
&NoCancellation,
40+
)
41+
}
42+
Dialect::TSX => {
43+
tree_sitter_stack_graphs_typescript::try_language_configuration_tsx(&NoCancellation)
44+
}
45+
}
46+
}
47+
2848
#[derive(Parser)]
2949
#[clap(about, version)]
3050
pub struct Cli {
51+
#[clap(
52+
short,
53+
long,
54+
value_enum,
55+
default_value_t = Dialect::Typescript,
56+
)]
57+
dialect: Dialect,
58+
3159
#[clap(subcommand)]
3260
subcommand: Subcommands,
3361
}

languages/tree-sitter-stack-graphs-typescript/rust/lib.rs

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ pub mod util;
1919
/// The stacks graphs tsg path for this language.
2020
pub const STACK_GRAPHS_TSG_PATH: &str = "src/stack-graphs.tsg";
2121
/// The stack graphs tsg source for this language
22-
pub const STACK_GRAPHS_TSG_SOURCE: &str = include_str!("../src/stack-graphs.tsg");
22+
pub const STACK_GRAPHS_TSG_TS_SOURCE: &str =
23+
include_str!(concat!(env!("OUT_DIR"), "/stack-graphs-typescript.tsg"));
24+
pub const STACK_GRAPHS_TSG_TSX_SOURCE: &str =
25+
include_str!(concat!(env!("OUT_DIR"), "/stack-graphs-tsx.tsg"));
2326

2427
/// The stack graphs builtins configuration for this language
2528
pub const STACK_GRAPHS_BUILTINS_CONFIG: &str = include_str!("../src/builtins.cfg");
@@ -33,11 +36,13 @@ pub const FILE_PATH_VAR: &str = "FILE_PATH";
3336
/// The name of the project name global variable
3437
pub const PROJECT_NAME_VAR: &str = "PROJECT_NAME";
3538

36-
pub fn language_configuration(cancellation_flag: &dyn CancellationFlag) -> LanguageConfiguration {
37-
try_language_configuration(cancellation_flag).unwrap_or_else(|err| panic!("{}", err))
39+
pub fn language_configuration_typescript(
40+
cancellation_flag: &dyn CancellationFlag,
41+
) -> LanguageConfiguration {
42+
try_language_configuration_typescript(cancellation_flag).unwrap_or_else(|err| panic!("{}", err))
3843
}
3944

40-
pub fn try_language_configuration(
45+
pub fn try_language_configuration_typescript(
4146
cancellation_flag: &dyn CancellationFlag,
4247
) -> Result<LanguageConfiguration, LoadError> {
4348
let mut lc = LanguageConfiguration::from_sources(
@@ -46,7 +51,37 @@ pub fn try_language_configuration(
4651
None,
4752
vec![String::from("ts")],
4853
STACK_GRAPHS_TSG_PATH.into(),
49-
STACK_GRAPHS_TSG_SOURCE,
54+
STACK_GRAPHS_TSG_TS_SOURCE,
55+
Some((
56+
STACK_GRAPHS_BUILTINS_PATH.into(),
57+
STACK_GRAPHS_BUILTINS_SOURCE,
58+
)),
59+
Some(STACK_GRAPHS_BUILTINS_CONFIG),
60+
cancellation_flag,
61+
)?;
62+
lc.special_files
63+
.add("tsconfig.json".to_string(), TsConfigAnalyzer {})
64+
.add("package.json".to_string(), NpmPackageAnalyzer {});
65+
lc.no_similar_paths_in_file = true;
66+
Ok(lc)
67+
}
68+
69+
pub fn language_configuration_tsx(
70+
cancellation_flag: &dyn CancellationFlag,
71+
) -> LanguageConfiguration {
72+
try_language_configuration_tsx(cancellation_flag).unwrap_or_else(|err| panic!("{}", err))
73+
}
74+
75+
pub fn try_language_configuration_tsx(
76+
cancellation_flag: &dyn CancellationFlag,
77+
) -> Result<LanguageConfiguration, LoadError> {
78+
let mut lc = LanguageConfiguration::from_sources(
79+
tree_sitter_typescript::language_tsx(),
80+
Some(String::from("source.tsx")),
81+
None,
82+
vec![String::from("tsx")],
83+
STACK_GRAPHS_TSG_PATH.into(),
84+
STACK_GRAPHS_TSG_TSX_SOURCE,
5085
Some((
5186
STACK_GRAPHS_BUILTINS_PATH.into(),
5287
STACK_GRAPHS_BUILTINS_SOURCE,

languages/tree-sitter-stack-graphs-typescript/rust/test.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ use tree_sitter_stack_graphs::ci::Tester;
1111
use tree_sitter_stack_graphs::NoCancellation;
1212

1313
fn main() -> anyhow::Result<()> {
14-
let lc = match tree_sitter_stack_graphs_typescript::try_language_configuration(&NoCancellation)
15-
{
14+
let lc = match tree_sitter_stack_graphs_typescript::try_language_configuration_typescript(
15+
&NoCancellation,
16+
) {
1617
Ok(lc) => lc,
1718
Err(err) => {
1819
eprintln!("{}", err.display_pretty());

languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2665,7 +2665,9 @@ if none @is_async {
26652665
(ternary_expression)
26662666
(this)
26672667
(true)
2668+
; #dialect typescript
26682669
(type_assertion)
2670+
; #end
26692671
(unary_expression)
26702672
(undefined)
26712673
(update_expression)
@@ -4040,7 +4042,7 @@ if none @is_async {
40404042
}
40414043

40424044

4043-
4045+
; #dialect typescript
40444046
;; Type Assertion
40454047

40464048
; (type_assertion
@@ -4055,7 +4057,7 @@ if none @is_async {
40554057
; propagate lexical scope
40564058
edge @expr.lexical_scope -> @type_assert.lexical_scope
40574059
}
4058-
4060+
; #end
40594061

40604062

40614063
;; As Expression

0 commit comments

Comments
 (0)