From 4c2bd0cc539e1fc170d019e073517b638ebbb294 Mon Sep 17 00:00:00 2001 From: pineapplestrikesback Date: Mon, 16 Mar 2026 16:23:18 +0100 Subject: [PATCH] feat: add Svelte and Vue import parsing to dependency graph MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Svelte and Vue files were included in the graph as leaf nodes (targets of import edges) but their own imports were never extracted because no ast-grep grammar exists for these languages. This adds support by parsing .svelte/.vue files as HTML (built-in grammar), extracting + + +`; + const imports = extractImports(source, "svelte", ".svelte"); + const specs = imports.map((i) => i.moduleSpecifier); + + expect(specs).toContain("svelte"); + expect(specs).toContain("./Button.svelte"); + expect(specs).toContain("../types.js"); + }); + + it("extracts imports from + + + +
content
+`; + const imports = extractImports(source, "svelte", ".svelte"); + const specs = imports.map((i) => i.moduleSpecifier); + + expect(specs).toContain("./Button.svelte"); + expect(specs).toContain("svelte"); + }); + + it("extracts dynamic imports from Svelte files", () => { + const source = ` + +`; + const imports = extractImports(source, "svelte", ".svelte"); + const dynamicImports = imports.filter((i) => i.isDynamic); + + expect(dynamicImports.length).toBeGreaterThanOrEqual(1); + expect( + dynamicImports.some( + (i) => i.moduleSpecifier === "./DynamicComponent.svelte", + ), + ).toBe(true); + }); + + it("handles Svelte files with no script block", () => { + const source = ` +
Just markup, no script
+ +`; + const imports = extractImports(source, "svelte", ".svelte"); + expect(imports).toHaveLength(0); + }); + + it("handles Svelte files with JavaScript (no lang=ts)", () => { + const source = ` + +`; + const imports = extractImports(source, "svelte", ".svelte"); + const specs = imports.map((i) => i.moduleSpecifier); + + expect(specs).toContain("svelte/store"); + expect(specs).toContain("./Item.svelte"); + }); + }); + + // ── Vue ──────────────────────────────────────────────────────────────── + + describe("Vue imports", () => { + it("extracts imports from + + +`; + const imports = extractImports(source, "vue", ".vue"); + const specs = imports.map((i) => i.moduleSpecifier); + + expect(specs).toContain("vue"); + expect(specs).toContain("./MyComponent.vue"); + }); + }); + // ── Python ───────────────────────────────────────────────────────────── describe("Python imports", () => { @@ -342,8 +450,12 @@ import kotlinx.coroutines.launch const specs = imports.map((i) => i.moduleSpecifier); expect(specs.length).toBeGreaterThanOrEqual(3); - expect(specs.some((s) => s.includes("com.example.models.User"))).toBe(true); - expect(specs.some((s) => s.includes("com.example.utils.StringHelper"))).toBe(true); + expect(specs.some((s) => s.includes("com.example.models.User"))).toBe( + true, + ); + expect( + specs.some((s) => s.includes("com.example.utils.StringHelper")), + ).toBe(true); }); it("handles wildcard imports", () => { @@ -353,7 +465,9 @@ import com.example.models.* const imports = extractImports(source, "kotlin", ".kt"); expect(imports.length).toBeGreaterThanOrEqual(1); - expect(imports.some((i) => i.moduleSpecifier.includes("com.example.models"))).toBe(true); + expect( + imports.some((i) => i.moduleSpecifier.includes("com.example.models")), + ).toBe(true); }); }); @@ -372,7 +486,11 @@ import com.example.services._ const specs = imports.map((i) => i.moduleSpecifier); expect(specs.length).toBeGreaterThanOrEqual(2); - expect(specs.some((s) => s.includes("scala.collection") || s.includes("ListBuffer"))).toBe(true); + expect( + specs.some( + (s) => s.includes("scala.collection") || s.includes("ListBuffer"), + ), + ).toBe(true); }); }); @@ -429,7 +547,9 @@ using static System.Math; const imports = extractImports(source, "csharp", ".cs"); expect(imports.length).toBeGreaterThanOrEqual(1); - expect(imports.some((i) => i.moduleSpecifier.includes("System.Math"))).toBe(true); + expect( + imports.some((i) => i.moduleSpecifier.includes("System.Math")), + ).toBe(true); }); it("skips using alias directives", () => {