From 71d517ea5ca6a33c9328f6b259847bc374017907 Mon Sep 17 00:00:00 2001 From: Devanshu Rajesh Chicholikar Date: Fri, 6 Mar 2026 15:41:09 -0500 Subject: [PATCH 1/2] fix: graceful fallback when tree-sitter-typescript not installed (production fix) The backend crashes on startup if tree-sitter-typescript is not installed because dependency_analyzer.py is imported at app init via dependencies.py. Fix: try/except around the import, fall back to JS parser (old behavior) if the TS parser package isn't available. App starts regardless. When tree-sitter-typescript IS installed, uses the proper TS/TSX parsers. When it's NOT installed, logs a warning and uses JS parser as fallback. This prevents Railway deploy failures from taking down the entire API. --- backend/services/dependency_analyzer.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/backend/services/dependency_analyzer.py b/backend/services/dependency_analyzer.py index a209383..01969ef 100644 --- a/backend/services/dependency_analyzer.py +++ b/backend/services/dependency_analyzer.py @@ -9,7 +9,11 @@ # Tree-sitter import tree_sitter_python as tspython import tree_sitter_javascript as tsjavascript -import tree_sitter_typescript as tstypescript +try: + import tree_sitter_typescript as tstypescript + _HAS_TS_PARSER = True +except ImportError: + _HAS_TS_PARSER = False from tree_sitter import Language, Parser from services.observability import logger, metrics @@ -19,14 +23,23 @@ class DependencyAnalyzer: """Analyze code dependencies and build dependency graph""" def __init__(self): - # Initialize parsers + js_lang = Language(tsjavascript.language()) + # Use proper TS parser if available, fall back to JS parser + if _HAS_TS_PARSER: + ts_lang = Language(tstypescript.language_typescript()) + tsx_lang = Language(tstypescript.language_tsx()) + else: + logger.warning("tree-sitter-typescript not installed, falling back to JS parser for TS/TSX") + ts_lang = js_lang + tsx_lang = js_lang + self.parsers = { 'python': Parser(Language(tspython.language())), - 'javascript': Parser(Language(tsjavascript.language())), - 'typescript': Parser(Language(tstypescript.language_typescript())), - 'tsx': Parser(Language(tstypescript.language_tsx())), + 'javascript': Parser(js_lang), + 'typescript': Parser(ts_lang), + 'tsx': Parser(tsx_lang), } - logger.info("DependencyAnalyzer initialized") + logger.info("DependencyAnalyzer initialized", ts_parser=_HAS_TS_PARSER) def _detect_language(self, file_path: str) -> str: """Detect language from file extension""" From 603cfcf55e30b5b7555e39474c3286a44d504a5c Mon Sep 17 00:00:00 2001 From: Devanshu Rajesh Chicholikar Date: Fri, 6 Mar 2026 15:52:57 -0500 Subject: [PATCH 2/2] fix: use ModuleNotFoundError, add has_ts_parser flag, add fallback test - except ImportError -> except ModuleNotFoundError (won't swallow real init errors from an installed-but-broken package) - Added self.has_ts_parser attribute for downstream consumers - Added test_fallback_when_ts_parser_missing: monkeypatches _HAS_TS_PARSER=False, verifies init succeeds, flag is False, and JS fallback still parses TS imports correctly --- backend/services/dependency_analyzer.py | 5 +++-- backend/tests/test_dependency_analyzer.py | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/backend/services/dependency_analyzer.py b/backend/services/dependency_analyzer.py index 01969ef..927efc6 100644 --- a/backend/services/dependency_analyzer.py +++ b/backend/services/dependency_analyzer.py @@ -12,7 +12,7 @@ try: import tree_sitter_typescript as tstypescript _HAS_TS_PARSER = True -except ImportError: +except ModuleNotFoundError: _HAS_TS_PARSER = False from tree_sitter import Language, Parser @@ -33,13 +33,14 @@ def __init__(self): ts_lang = js_lang tsx_lang = js_lang + self.has_ts_parser = _HAS_TS_PARSER self.parsers = { 'python': Parser(Language(tspython.language())), 'javascript': Parser(js_lang), 'typescript': Parser(ts_lang), 'tsx': Parser(tsx_lang), } - logger.info("DependencyAnalyzer initialized", ts_parser=_HAS_TS_PARSER) + logger.info("DependencyAnalyzer initialized", ts_parser=self.has_ts_parser) def _detect_language(self, file_path: str) -> str: """Detect language from file extension""" diff --git a/backend/tests/test_dependency_analyzer.py b/backend/tests/test_dependency_analyzer.py index 772b405..317dfe1 100644 --- a/backend/tests/test_dependency_analyzer.py +++ b/backend/tests/test_dependency_analyzer.py @@ -103,6 +103,23 @@ def test_ts_parser_is_not_js(self, analyzer): # They should be different Language objects assert ts_parser is not js_parser + def test_has_ts_parser_flag(self, analyzer): + assert analyzer.has_ts_parser is True + + def test_fallback_when_ts_parser_missing(self, monkeypatch, tmp_path): + """App must not crash if tree-sitter-typescript is missing""" + import services.dependency_analyzer as mod + monkeypatch.setattr(mod, '_HAS_TS_PARSER', False) + from services.dependency_analyzer import DependencyAnalyzer + fallback = DependencyAnalyzer() + # Should initialize without error + assert fallback.has_ts_parser is False + # Should still parse TS files (using JS fallback) + ts_file = tmp_path / "test.ts" + ts_file.write_text('import { foo } from "./bar"') + result = fallback.analyze_file_dependencies(str(ts_file)) + assert './bar' in result['imports'] + class TestLanguageDetection: """Verify file extension to language mapping"""