diff --git a/graphify/detect.py b/graphify/detect.py index 4cb123104c..a2b770b87c 100644 --- a/graphify/detect.py +++ b/graphify/detect.py @@ -42,7 +42,7 @@ class FileType(str, Enum): _MTIME_COARSE_S = 2.0 _MTIME_SUBSECOND_S = 0.05 -CODE_EXTENSIONS = {'.py', '.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs', '.ejs', '.ets', '.go', '.rs', '.java', '.groovy', '.gradle', '.cpp', '.cc', '.cxx', '.c', '.h', '.hpp', '.cu', '.cuh', '.metal', '.rb', '.rake', '.swift', '.kt', '.kts', '.cs', '.scala', '.php', '.lua', '.luau', '.toc', '.zig', '.ps1', '.psm1', '.psd1', '.ex', '.exs', '.m', '.mm', '.ml', '.mli', '.jl', '.vue', '.svelte', '.astro', '.dart', '.v', '.sv', '.svh', '.sql', '.r', '.f', '.F', '.f90', '.F90', '.f95', '.F95', '.f03', '.F03', '.f08', '.F08', '.pas', '.pp', '.dpr', '.dpk', '.lpr', '.inc', '.dfm', '.lfm', '.lpk', '.sh', '.bash', '.json', '.tf', '.tfvars', '.hcl', '.dm', '.dme', '.dmi', '.dmm', '.dmf', '.sln', '.slnx', '.csproj', '.fsproj', '.vbproj', '.xaml', '.razor', '.cshtml', '.cls', '.trigger', '.lisp', '.cl', '.lsp', '.asd'} +CODE_EXTENSIONS = {'.py', '.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs', '.gs', '.ejs', '.ets', '.go', '.rs', '.java', '.groovy', '.gradle', '.cpp', '.cc', '.cxx', '.c', '.h', '.hpp', '.cu', '.cuh', '.metal', '.rb', '.rake', '.swift', '.kt', '.kts', '.cs', '.scala', '.php', '.lua', '.luau', '.toc', '.zig', '.ps1', '.psm1', '.psd1', '.ex', '.exs', '.m', '.mm', '.ml', '.mli', '.jl', '.vue', '.svelte', '.astro', '.dart', '.v', '.sv', '.svh', '.sql', '.r', '.f', '.F', '.f90', '.F90', '.f95', '.F95', '.f03', '.F03', '.f08', '.F08', '.pas', '.pp', '.dpr', '.dpk', '.lpr', '.inc', '.dfm', '.lfm', '.lpk', '.sh', '.bash', '.json', '.tf', '.tfvars', '.hcl', '.dm', '.dme', '.dmi', '.dmm', '.dmf', '.sln', '.slnx', '.csproj', '.fsproj', '.vbproj', '.xaml', '.razor', '.cshtml', '.cls', '.trigger', '.lisp', '.cl', '.lsp', '.asd'} DOC_EXTENSIONS = {'.md', '.mdx', '.qmd', '.skill', '.txt', '.rst', '.html', '.yaml', '.yml'} PAPER_EXTENSIONS = {'.pdf'} IMAGE_EXTENSIONS = {'.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg'} diff --git a/graphify/extract.py b/graphify/extract.py index a113d4a7ac..2ce46d0eb4 100644 --- a/graphify/extract.py +++ b/graphify/extract.py @@ -2105,7 +2105,7 @@ def _lang_is_case_insensitive(source_file: object) -> bool: # no family and are never filtered — same permissive default as before. _LANG_FAMILY_BY_EXT: dict[str, str] = { # JS/TS module graph (SFCs embed JS/TS) - ".js": "jsts", ".jsx": "jsts", ".mjs": "jsts", ".cjs": "jsts", + ".js": "jsts", ".jsx": "jsts", ".mjs": "jsts", ".cjs": "jsts", ".gs": "jsts", ".ts": "jsts", ".tsx": "jsts", ".mts": "jsts", ".cts": "jsts", ".vue": "jsts", ".svelte": "jsts", ".astro": "jsts", # JVM interop @@ -4872,6 +4872,7 @@ def add_existing_edge(edge: dict) -> None: ".jsx": extract_js, ".mjs": extract_js, ".cjs": extract_js, + ".gs": extract_js, ".ts": extract_js, ".tsx": extract_js, ".mts": extract_js, diff --git a/graphify/extractors/models.py b/graphify/extractors/models.py index 63c1d8a181..6ea2abb5f3 100644 --- a/graphify/extractors/models.py +++ b/graphify/extractors/models.py @@ -8,7 +8,7 @@ _WORKSPACE_PACKAGE_CACHE: dict[str, dict[str, Path]] = {} -_JS_CACHE_BYPASS_SUFFIXES = {".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx", ".mts", ".cts", ".vue", ".svelte"} +_JS_CACHE_BYPASS_SUFFIXES = {".js", ".jsx", ".mjs", ".cjs", ".gs", ".ts", ".tsx", ".mts", ".cts", ".vue", ".svelte"} @dataclass class LanguageConfig: diff --git a/tests/test_detect.py b/tests/test_detect.py index 5be697ce72..8cba083eab 100644 --- a/tests/test_detect.py +++ b/tests/test_detect.py @@ -34,6 +34,10 @@ def test_classify_python(): def test_classify_typescript(): assert classify_file(Path("bar.ts")) == FileType.CODE +def test_classify_google_apps_script(): + # Google Apps Script uses JavaScript syntax and the JS extractor. + assert classify_file(Path("automation.gs")) == FileType.CODE + def test_classify_powershell_module(): # #1315: .psm1 modules were never indexed (CODE_EXTENSIONS gap). assert classify_file(Path("Utils.psm1")) == FileType.CODE diff --git a/tests/test_extract.py b/tests/test_extract.py index 416d358280..5a28f8b712 100644 --- a/tests/test_extract.py +++ b/tests/test_extract.py @@ -2231,10 +2231,24 @@ def wrapped_sequential(uncached_work, per_file, *args, **kwargs): # Bash extractor tests (#866) # --------------------------------------------------------------------------- -def test_dispatch_includes_sh_and_json(): +def test_dispatch_includes_sh_json_and_google_apps_script(): assert ".sh" in _DISPATCH assert ".bash" in _DISPATCH assert ".json" in _DISPATCH + assert ".gs" in _DISPATCH + + +def test_extract_google_apps_script_as_javascript(tmp_path): + """Google Apps Script files must enter the JS extractor instead of being skipped.""" + script = tmp_path / "automation.gs" + script.write_text( + "function synchronizeSheet() { return SpreadsheetApp.getActive(); }\n", + encoding="utf-8", + ) + + result = extract([script], cache_root=tmp_path, root=tmp_path, parallel=False) + + assert "synchronizeSheet()" in {node["label"] for node in result["nodes"]} def test_extract_bash_finds_functions():