Add AST extraction tests, broader ignore patterns, build dist
- ast-extract.ts: Support tree-sitter-python and tree-sitter-go grammar loading (pkg.language fallback) - discover.ts: Add cache directories to default ignore (.cache, tmp, temp, .turbo, .parcel-cache, .eslintcache, .prettiercache) - tests: Add TypeScript AST extraction tests (exports + imports) - Verified on real project: media_library_viewer (196 .pi-map.md files) with accurate React component extraction - Build: npm run build produces clean dist/ output All 16 tests pass.
This commit is contained in:
Generated
+40
@@ -11,6 +11,8 @@
|
||||
"dependencies": {
|
||||
"ignore": "^5.3.0",
|
||||
"tree-sitter": "^0.21.0",
|
||||
"tree-sitter-go": "^0.25.0",
|
||||
"tree-sitter-python": "^0.25.0",
|
||||
"tree-sitter-typescript": "^0.21.0"
|
||||
},
|
||||
"bin": {
|
||||
@@ -3318,6 +3320,44 @@
|
||||
"node-gyp-build": "^4.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tree-sitter-go": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/tree-sitter-go/-/tree-sitter-go-0.25.0.tgz",
|
||||
"integrity": "sha512-APBc/Dq3xz/e35Xpkhb1blu5UgW+2E3RyGWawZSCNcbGwa7jhSQPS8KsUupuzBla8PCo8+lz9W/JDJjmfRa2tw==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"node-addon-api": "^8.3.1",
|
||||
"node-gyp-build": "^4.8.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"tree-sitter": "^0.25.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"tree-sitter": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/tree-sitter-python": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/tree-sitter-python/-/tree-sitter-python-0.25.0.tgz",
|
||||
"integrity": "sha512-eCmJx6zQa35GxaCtQD+wXHOhYqBxEL+bp71W/s3fcDMu06MrtzkVXR437dRrCrbrDbyLuUDJpAgycs7ncngLXw==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"node-addon-api": "^8.5.0",
|
||||
"node-gyp-build": "^4.8.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"tree-sitter": "^0.25.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"tree-sitter": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/tree-sitter-typescript": {
|
||||
"version": "0.21.2",
|
||||
"resolved": "https://registry.npmjs.org/tree-sitter-typescript/-/tree-sitter-typescript-0.21.2.tgz",
|
||||
|
||||
@@ -35,6 +35,8 @@
|
||||
"dependencies": {
|
||||
"ignore": "^5.3.0",
|
||||
"tree-sitter": "^0.21.0",
|
||||
"tree-sitter-go": "^0.25.0",
|
||||
"tree-sitter-python": "^0.25.0",
|
||||
"tree-sitter-typescript": "^0.21.0"
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -40,7 +40,8 @@ export async function extractFileAST(
|
||||
// For other languages, try to require the grammar package
|
||||
try {
|
||||
const pkg = require(`tree-sitter-${langName}`);
|
||||
grammar = pkg.default || pkg;
|
||||
// Some packages export { language }, others export the grammar directly
|
||||
grammar = pkg.language || pkg.default || pkg;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,14 @@ const DEFAULT_IGNORE = [
|
||||
".DS_Store",
|
||||
"*.log",
|
||||
".pi-map.md",
|
||||
".cache",
|
||||
"tmp",
|
||||
"temp",
|
||||
".tmp",
|
||||
".turbo",
|
||||
".parcel-cache",
|
||||
".eslintcache",
|
||||
".prettiercache",
|
||||
];
|
||||
|
||||
export interface DirectoryEntry {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { extractFileAST } from "../src/ast-extract.js";
|
||||
import { mkdtempSync, writeFileSync } from "fs";
|
||||
import { join } from "path";
|
||||
import { tmpdir } from "os";
|
||||
|
||||
describe("ast-extract", () => {
|
||||
it("extracts TypeScript exports and imports", async () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), "pi-map-ast-"));
|
||||
const file = join(dir, "test.ts");
|
||||
writeFileSync(
|
||||
file,
|
||||
`import { foo } from "./bar";
|
||||
import type { Qux } from "qux-lib";
|
||||
|
||||
export function hello() {}
|
||||
export class MyClass {}
|
||||
export const value = 1;
|
||||
export interface Config {}
|
||||
export type MyType = string;
|
||||
export { foo as renamedFoo };
|
||||
`,
|
||||
);
|
||||
const result = await extractFileAST(file);
|
||||
expect(result).not.toBeNull();
|
||||
expect(result!.exports).toContain("hello");
|
||||
expect(result!.exports).toContain("MyClass");
|
||||
expect(result!.exports).toContain("value");
|
||||
expect(result!.exports).toContain("Config");
|
||||
expect(result!.exports).toContain("MyType");
|
||||
expect(result!.deps).toContain("./bar");
|
||||
expect(result!.deps).toContain("qux-lib");
|
||||
});
|
||||
|
||||
it("returns null for unsupported languages", async () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), "pi-map-ast-"));
|
||||
const file = join(dir, "test.xyz");
|
||||
writeFileSync(file, `some content`);
|
||||
const result = await extractFileAST(file);
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
+17
-15
@@ -1,5 +1,11 @@
|
||||
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
||||
import { mkdtempSync, writeFileSync, mkdirSync, rmSync, readFileSync } from "fs";
|
||||
import {
|
||||
mkdtempSync,
|
||||
writeFileSync,
|
||||
mkdirSync,
|
||||
rmSync,
|
||||
readFileSync,
|
||||
} from "fs";
|
||||
import { join } from "path";
|
||||
import { tmpdir } from "os";
|
||||
import { initProject } from "../src/init.js";
|
||||
@@ -19,10 +25,7 @@ describe("integration", () => {
|
||||
|
||||
it("init creates .pi-map.md files", async () => {
|
||||
mkdirSync(join(dir, "src"));
|
||||
writeFileSync(
|
||||
join(dir, "src", "index.ts"),
|
||||
`export function foo() {}\n`,
|
||||
);
|
||||
writeFileSync(join(dir, "src", "index.ts"), `export function foo() {}\n`);
|
||||
await initProject(dir);
|
||||
|
||||
const map = readFileSync(join(dir, "src", ".pi-map.md"), "utf8");
|
||||
@@ -32,14 +35,8 @@ describe("integration", () => {
|
||||
|
||||
it("patch updates a file entry", async () => {
|
||||
mkdirSync(join(dir, "src"));
|
||||
writeFileSync(
|
||||
join(dir, "src", "index.ts"),
|
||||
`export function foo() {}\n`,
|
||||
);
|
||||
writeFileSync(
|
||||
join(dir, "src", "utils.ts"),
|
||||
`export const bar = 1;\n`,
|
||||
);
|
||||
writeFileSync(join(dir, "src", "index.ts"), `export function foo() {}\n`);
|
||||
writeFileSync(join(dir, "src", "utils.ts"), `export const bar = 1;\n`);
|
||||
await initProject(dir);
|
||||
|
||||
// Modify a file
|
||||
@@ -110,10 +107,15 @@ describe("integration", () => {
|
||||
await initProject(dir);
|
||||
|
||||
// Change exports
|
||||
writeFileSync(join(dir, "src", "a.ts"), `export const a = 1;\nexport const c = 3;\n`);
|
||||
writeFileSync(
|
||||
join(dir, "src", "a.ts"),
|
||||
`export const a = 1;\nexport const c = 3;\n`,
|
||||
);
|
||||
|
||||
const result = await validateMaps(dir);
|
||||
expect(result.clean).toBe(false);
|
||||
expect(result.discrepancies.some((d) => d.type === "stale-signature")).toBe(true);
|
||||
expect(result.discrepancies.some((d) => d.type === "stale-signature")).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user