Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,8 @@ compiler-grade layer, all `$0` and deterministic (no model, no key):
call edges via a generic tree-sitter extractor, one grammar per language:
**Rust, C, C++, C#, Ruby, Scala, Elixir, Solidity,
Dart, Clojure, Nix, Lua, Gleam, Julia**, and **OCaml, Zig, GLSL** (symbols only for now, no call edges).
**Bash** (`.sh`, `.bash`) also uses this tier for function symbols;
Bash call edges and extensionless shell scripts are not yet supported.

- **Single-file components**: **Vue** (`.vue`), **Svelte** (`.svelte`) and
**Astro** (`.astro`). The wrapper grammar only locates the `<script>` block
Expand Down
1 change: 1 addition & 0 deletions src/graph/generic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export const GENERIC_LANGS: readonly GenericLang[] = [
{ name: "powershell", exts: [".ps1", ".psm1"], wasm: "powershell" },
{ name: "julia", exts: [".jl"], wasm: "julia" },
{ name: "glsl", exts: [".glsl"], wasm: "glsl" }, // #293; gdscript is #299
{ name: "bash", exts: [".sh", ".bash"], wasm: "bash" },
];

/**
Expand Down
60 changes: 60 additions & 0 deletions test/graph-bash.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { test } from "node:test";
import assert from "node:assert/strict";
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { buildGraph } from "../src/graph/build.js";
import { checkGraph } from "../src/graph/check.js";
import { extractGeneric, genericLangOf, isWarm, warmGenericGrammars } from "../src/graph/generic.js";
import { unsupportedExtensions } from "../src/graph/source-files.js";
import { readGraph, wiringPath } from "../src/graph/write.js";

const BASH = `#!/usr/bin/env bash
greet() {
printf '%s\\n' "hello"
}

function main {
greet
}
`;

test("Bash extensions load the bundled grammar and extract both function syntaxes", async () => {
for (const file of ["setup.sh", "library.bash", "SETUP.SH"]) {
assert.equal(genericLangOf(file)?.name, "bash");
}
assert.deepEqual(unsupportedExtensions([".sh", ".bash", "SH"]), []);
await warmGenericGrammars(["bash"]);
assert.ok(isWarm("bash"), "the bundled Bash grammar loads");
const { nodes } = extractGeneric("setup.sh", BASH, "bash");
assert.deepEqual(
nodes.filter((node) => node.kind !== "file").map((node) => [node.name, node.kind, node.span]),
[["greet", "function", "L2-L4"], ["main", "function", "L6-L8"]],
);
});

test("buildGraph indexes Bash files and checkGraph reports them in sync", async () => {
const dir = mkdtempSync(join(tmpdir(), "graft-bash-"));
try {
writeFileSync(join(dir, "setup.sh"), BASH);
writeFileSync(join(dir, "library.bash"), "helper() { echo ok; }\n");
writeFileSync(join(dir, "extensionless"), BASH);

const result = await buildGraph(dir, { reuse: false });
assert.deepEqual(result.languages, ["bash"]);
assert.deepEqual(result.errors, []);
const graph = readGraph(wiringPath(result.contextDir));
assert.ok(graph);
assert.deepEqual(
graph.nodes.filter((node) => node.kind === "file").map((node) => node.path).sort(),
["library.bash", "setup.sh"],
);
assert.deepEqual(
graph.nodes.filter((node) => node.kind === "function").map((node) => node.name).sort(),
["greet", "helper", "main"],
);
assert.equal((await checkGraph(dir)).ok, true);
} finally {
rmSync(dir, { recursive: true, force: true });
}
});
Loading