diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 670b8b9e..736fb7cb 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -89,15 +89,18 @@ jobs: - name: Run build benchmark if: steps.existing.outputs.skip != 'true' run: | + STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") ARGS="--version ${{ steps.mode.outputs.version }}" if [ "${{ steps.mode.outputs.source }}" = "npm" ]; then ARGS="$ARGS --npm" fi - node scripts/benchmark.js $ARGS 2>/dev/null > benchmark-result.json + node $STRIP_FLAG --import ./scripts/ts-resolve-loader.js scripts/benchmark.ts $ARGS 2>/dev/null > benchmark-result.json - name: Update build report if: steps.existing.outputs.skip != 'true' - run: node scripts/update-benchmark-report.js benchmark-result.json + run: | + STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") + node $STRIP_FLAG scripts/update-benchmark-report.ts benchmark-result.json - name: Upload build result if: steps.existing.outputs.skip != 'true' @@ -240,15 +243,18 @@ jobs: env: HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | + STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") ARGS="--version ${{ steps.mode.outputs.version }}" if [ "${{ steps.mode.outputs.source }}" = "npm" ]; then ARGS="$ARGS --npm" fi - node scripts/embedding-benchmark.js $ARGS 2>/dev/null > embedding-benchmark-result.json + node $STRIP_FLAG --import ./scripts/ts-resolve-loader.js scripts/embedding-benchmark.ts $ARGS 2>/dev/null > embedding-benchmark-result.json - name: Update embedding report if: steps.existing.outputs.skip != 'true' - run: node scripts/update-embedding-report.js embedding-benchmark-result.json + run: | + STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") + node $STRIP_FLAG scripts/update-embedding-report.ts embedding-benchmark-result.json - name: Upload embedding result if: steps.existing.outputs.skip != 'true' @@ -377,15 +383,18 @@ jobs: - name: Run query benchmark if: steps.existing.outputs.skip != 'true' run: | + STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") ARGS="--version ${{ steps.mode.outputs.version }}" if [ "${{ steps.mode.outputs.source }}" = "npm" ]; then ARGS="$ARGS --npm" fi - node scripts/query-benchmark.js $ARGS 2>/dev/null > query-benchmark-result.json + node $STRIP_FLAG --import ./scripts/ts-resolve-loader.js scripts/query-benchmark.ts $ARGS 2>/dev/null > query-benchmark-result.json - name: Update query report if: steps.existing.outputs.skip != 'true' - run: node scripts/update-query-report.js query-benchmark-result.json + run: | + STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") + node $STRIP_FLAG scripts/update-query-report.ts query-benchmark-result.json - name: Upload query result if: steps.existing.outputs.skip != 'true' @@ -514,15 +523,18 @@ jobs: - name: Run incremental benchmark if: steps.existing.outputs.skip != 'true' run: | + STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") ARGS="--version ${{ steps.mode.outputs.version }}" if [ "${{ steps.mode.outputs.source }}" = "npm" ]; then ARGS="$ARGS --npm" fi - node scripts/incremental-benchmark.js $ARGS 2>/dev/null > incremental-benchmark-result.json + node $STRIP_FLAG --import ./scripts/ts-resolve-loader.js scripts/incremental-benchmark.ts $ARGS 2>/dev/null > incremental-benchmark-result.json - name: Update incremental report if: steps.existing.outputs.skip != 'true' - run: node scripts/update-incremental-report.js incremental-benchmark-result.json + run: | + STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") + node $STRIP_FLAG scripts/update-incremental-report.ts incremental-benchmark-result.json - name: Upload incremental result if: steps.existing.outputs.skip != 'true' diff --git a/.github/workflows/build-native.yml b/.github/workflows/build-native.yml index fb6760a2..77eb428d 100644 --- a/.github/workflows/build-native.yml +++ b/.github/workflows/build-native.yml @@ -60,7 +60,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v6 with: - node-version: 20 + node-version: 22 - name: Setup Rust uses: dtolnay/rust-toolchain@stable diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a80097ce..26bd4f16 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] - node-version: [20, 22] + node-version: [22] runs-on: ${{ matrix.os }} name: Test Node ${{ matrix.node-version }} (${{ matrix.os }}) @@ -113,7 +113,9 @@ jobs: node-version: 22 - name: Verify all dynamic imports resolve - run: node scripts/verify-imports.js + run: | + STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") + node $STRIP_FLAG scripts/verify-imports.ts rust-check: runs-on: ubuntu-latest diff --git a/.github/workflows/embedding-regression.yml b/.github/workflows/embedding-regression.yml index a42cc6e7..e049911d 100644 --- a/.github/workflows/embedding-regression.yml +++ b/.github/workflows/embedding-regression.yml @@ -38,4 +38,4 @@ jobs: key: hf-models-minilm-v1 - name: Run embedding regression tests - run: npx vitest run tests/search/embedding-regression.test.js + run: npx vitest run tests/search/embedding-regression.test.ts diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index df34445c..af3e79b7 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -142,7 +142,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v6 with: - node-version: 20 + node-version: 22 - name: Setup Rust uses: dtolnay/rust-toolchain@stable @@ -224,7 +224,8 @@ jobs: VERSION: ${{ needs.compute-version.outputs.version }} run: | npm version "$VERSION" --no-git-tag-version --allow-same-version - node scripts/sync-native-versions.js --strip + STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") + node $STRIP_FLAG scripts/sync-native-versions.ts --strip echo "Packaging version $VERSION" - name: Build TypeScript @@ -405,7 +406,8 @@ jobs: run: | git checkout -- package-lock.json npm version "$VERSION" --no-git-tag-version --allow-same-version - node scripts/sync-native-versions.js + STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") + node $STRIP_FLAG scripts/sync-native-versions.ts echo "Publishing version $VERSION" - name: Build TypeScript diff --git a/.npmignore b/.npmignore index 733a7a49..3977f0ea 100644 --- a/.npmignore +++ b/.npmignore @@ -7,5 +7,5 @@ docs/ *.db .codegraphrc.json .versionrc.json -commitlint.config.js +commitlint.config.ts codegraph-improvements.md diff --git a/CLAUDE.md b/CLAUDE.md index 6d441c78..5c97e60a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -218,4 +218,4 @@ This repo uses [Greptile](https://greptile.com) for automated PR reviews. After ## Node Version -Requires Node >= 20. +Requires Node >= 22.6. diff --git a/commitlint.config.js b/commitlint.config.ts similarity index 100% rename from commitlint.config.js rename to commitlint.config.ts diff --git a/docs/examples/claude-code-hooks/pre-commit-checks.js b/docs/examples/claude-code-hooks/pre-commit-checks.ts similarity index 95% rename from docs/examples/claude-code-hooks/pre-commit-checks.js rename to docs/examples/claude-code-hooks/pre-commit-checks.ts index f6bd6cfb..c863d8fd 100644 --- a/docs/examples/claude-code-hooks/pre-commit-checks.js +++ b/docs/examples/claude-code-hooks/pre-commit-checks.ts @@ -1,8 +1,8 @@ /** - * pre-commit-checks.js — Consolidated pre-commit codegraph checks. + * pre-commit-checks.ts — Consolidated pre-commit codegraph checks. * Single Node.js process that runs all checks and returns structured JSON. * - * Usage: node pre-commit-checks.js + * Usage: node pre-commit-checks.ts * * Output JSON: { action: "deny"|"allow", reason?: string, context?: string[] } * @@ -12,8 +12,11 @@ * 3. Diff-impact (informational) — shows blast radius of staged changes */ -const fs = require('fs'); -const path = require('path'); +import fs from 'node:fs'; +import path from 'node:path'; +import { createRequire } from 'node:module'; + +const require = createRequire(import.meta.url); const root = process.argv[2]; const editedRaw = process.argv[3] || ''; @@ -124,7 +127,7 @@ try { // Scan for dynamic import() consumers const srcDir = path.join(root, 'src'); - function scanDynamic(dir) { + function scanDynamic(dir: string) { for (const ent of fs.readdirSync(dir, { withFileTypes: true })) { if (ent.isDirectory()) { scanDynamic(path.join(dir, ent.name)); continue; } if (!/\.(js|ts|tsx)$/.test(ent.name)) continue; diff --git a/docs/examples/claude-code-hooks/pre-commit.sh b/docs/examples/claude-code-hooks/pre-commit.sh index dfa1f3d2..96f3e23f 100644 --- a/docs/examples/claude-code-hooks/pre-commit.sh +++ b/docs/examples/claude-code-hooks/pre-commit.sh @@ -48,7 +48,8 @@ fi # Run all checks in a single Node.js process HOOK_DIR="$(cd "$(dirname "$0")" && pwd)" -RESULT=$(node "$HOOK_DIR/pre-commit-checks.js" "$WORK_ROOT" "$EDITED_FILES" "$STAGED" 2>/dev/null) || true +STRIP_FLAG=$(node -e "const [M,m]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") +RESULT=$(node $STRIP_FLAG "$HOOK_DIR/pre-commit-checks.ts" "$WORK_ROOT" "$EDITED_FILES" "$STAGED" 2>/dev/null) || true if [ -z "$RESULT" ]; then exit 0 diff --git a/package.json b/package.json index 01115f82..36805d53 100644 --- a/package.json +++ b/package.json @@ -26,26 +26,26 @@ "README.md" ], "engines": { - "node": ">=20" + "node": ">=22.6" }, "scripts": { - "build": "tsc", - "build:wasm": "node scripts/build-wasm.js", + "build": "tsc && node -e \"require('fs').writeFileSync('dist/index.cjs',require('fs').readFileSync('src/index.cjs','utf8').replaceAll('./index.ts','./index.js'))\"", + "build:wasm": "node --experimental-strip-types scripts/build-wasm.ts", "typecheck": "tsc --noEmit", - "verify-imports": "node scripts/verify-imports.js", - "test": "node scripts/test.js run", - "test:watch": "node scripts/test.js", - "test:coverage": "node scripts/test.js run --coverage", + "verify-imports": "node --experimental-strip-types scripts/verify-imports.ts", + "test": "vitest run", + "test:watch": "vitest", + "test:coverage": "vitest run --coverage", "lint": "biome check src/ tests/", "lint:fix": "biome check --write src/ tests/", "format": "biome format --write src/ tests/", "prepack": "npm run build", "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true});require('fs').rmSync('.tsbuildinfo',{force:true})\"", "prepare": "npm run build:wasm && npm run build && husky && npm run deps:tree", - "deps:tree": "node scripts/gen-deps.cjs", + "deps:tree": "node --experimental-strip-types scripts/gen-deps.ts", "release": "commit-and-tag-version", "release:dry-run": "commit-and-tag-version --dry-run", - "version": "node scripts/sync-native-versions.js && git add package.json crates/codegraph-core/Cargo.toml" + "version": "node --experimental-strip-types scripts/sync-native-versions.ts && git add package.json crates/codegraph-core/Cargo.toml" }, "keywords": [ "codegraph", diff --git a/scripts/bench-version.js b/scripts/bench-version.ts similarity index 100% rename from scripts/bench-version.js rename to scripts/bench-version.ts diff --git a/scripts/benchmark.js b/scripts/benchmark.ts similarity index 100% rename from scripts/benchmark.js rename to scripts/benchmark.ts diff --git a/scripts/build-wasm.js b/scripts/build-wasm.ts similarity index 100% rename from scripts/build-wasm.js rename to scripts/build-wasm.ts diff --git a/scripts/embedding-benchmark.js b/scripts/embedding-benchmark.ts similarity index 100% rename from scripts/embedding-benchmark.js rename to scripts/embedding-benchmark.ts diff --git a/scripts/gen-deps.cjs b/scripts/gen-deps.ts similarity index 51% rename from scripts/gen-deps.cjs rename to scripts/gen-deps.ts index 32072004..6c7ec148 100644 --- a/scripts/gen-deps.cjs +++ b/scripts/gen-deps.ts @@ -1,18 +1,19 @@ -const { execSync } = require('child_process'); -const fs = require('fs'); -const path = require('path'); +#!/usr/bin/env node +import { execSync } from 'node:child_process'; +import { mkdirSync, writeFileSync } from 'node:fs'; +import { dirname, join } from 'node:path'; -const outFile = path.join('generated', 'DEPENDENCIES.md'); -fs.mkdirSync(path.dirname(outFile), { recursive: true }); +const outFile = join('generated', 'DEPENDENCIES.md'); +mkdirSync(dirname(outFile), { recursive: true }); try { const tree = execSync('npm ls --all --omit=dev', { encoding: 'utf8' }); - fs.writeFileSync(outFile, '# Dependencies\n\n```\n' + tree + '```\n'); -} catch (err) { + writeFileSync(outFile, '# Dependencies\n\n```\n' + tree + '```\n'); +} catch (err: any) { // npm ls exits non-zero on ELSPROBLEMS (version mismatches in optional deps). // If stdout still has content, write it; otherwise skip silently. if (err.stdout) { - fs.writeFileSync( + writeFileSync( outFile, '# Dependencies\n\n```\n' + err.stdout + '```\n', ); diff --git a/scripts/incremental-benchmark.js b/scripts/incremental-benchmark.ts similarity index 100% rename from scripts/incremental-benchmark.js rename to scripts/incremental-benchmark.ts diff --git a/scripts/lib/bench-config.js b/scripts/lib/bench-config.ts similarity index 100% rename from scripts/lib/bench-config.js rename to scripts/lib/bench-config.ts diff --git a/scripts/lib/fork-engine.js b/scripts/lib/fork-engine.ts similarity index 100% rename from scripts/lib/fork-engine.js rename to scripts/lib/fork-engine.ts diff --git a/scripts/query-benchmark.js b/scripts/query-benchmark.ts similarity index 100% rename from scripts/query-benchmark.js rename to scripts/query-benchmark.ts diff --git a/scripts/sync-native-versions.js b/scripts/sync-native-versions.ts similarity index 100% rename from scripts/sync-native-versions.js rename to scripts/sync-native-versions.ts diff --git a/scripts/test.js b/scripts/test.js deleted file mode 100644 index ea317a7d..00000000 --- a/scripts/test.js +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env node -/** - * Test runner wrapper that configures Node.js for the TypeScript migration - * before spawning vitest. Adds --experimental-strip-types on Node >= 22.6 - * so child processes can execute .ts files natively. - */ - -import { spawnSync } from 'node:child_process'; -import { fileURLToPath, pathToFileURL } from 'node:url'; -import { dirname, resolve } from 'node:path'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); -const hook = pathToFileURL(resolve(__dirname, 'ts-resolver-hook.js')).href; - -const args = process.argv.slice(2); -const vitestBin = resolve(__dirname, '..', 'node_modules', 'vitest', 'vitest.mjs'); - -const [major, minor] = process.versions.node.split('.').map(Number); -const supportsStripTypes = major > 22 || (major === 22 && minor >= 6); - -// Build NODE_OPTIONS: resolver hook + type stripping (Node >= 22.6) -const hookImport = `--import ${hook}`; -const existing = process.env.NODE_OPTIONS || ''; -const parts = [ - existing.includes(hookImport) ? null : hookImport, - supportsStripTypes && !existing.includes('--experimental-strip-types') && !existing.includes('--strip-types') - ? (major >= 23 ? '--strip-types' : '--experimental-strip-types') - : null, - existing || null, -].filter(Boolean); - -// Spawn vitest via node directly — avoids shell: true and works cross-platform -const result = spawnSync(process.execPath, [vitestBin, ...args], { - stdio: 'inherit', - env: { - ...process.env, - NODE_OPTIONS: parts.join(' '), - }, -}); - -if (result.error) { - process.stderr.write(`[test runner] Failed to spawn vitest: ${result.error.message}\n`); - process.exit(1); -} - -process.exit(result.status ?? 1); diff --git a/scripts/token-benchmark-issues.js b/scripts/token-benchmark-issues.ts similarity index 100% rename from scripts/token-benchmark-issues.js rename to scripts/token-benchmark-issues.ts diff --git a/scripts/token-benchmark.js b/scripts/token-benchmark.ts similarity index 98% rename from scripts/token-benchmark.js rename to scripts/token-benchmark.ts index 1598e6dc..02e053bc 100644 --- a/scripts/token-benchmark.js +++ b/scripts/token-benchmark.ts @@ -12,9 +12,9 @@ * ANTHROPIC_API_KEY set in environment * * Usage: - * node scripts/token-benchmark.js > result.json - * node scripts/token-benchmark.js --runs 1 --issues csrf-case-insensitive - * node scripts/token-benchmark.js --nextjs-dir /tmp/next.js --skip-graph + * node --experimental-strip-types --import ./scripts/ts-resolve-loader.js scripts/token-benchmark.ts > result.json + * node --experimental-strip-types --import ./scripts/ts-resolve-loader.js scripts/token-benchmark.ts --runs 1 --issues csrf-case-insensitive + * node --experimental-strip-types --import ./scripts/ts-resolve-loader.js scripts/token-benchmark.ts --nextjs-dir /tmp/next.js --skip-graph */ import { execFileSync, execSync } from 'node:child_process'; diff --git a/scripts/ts-resolve-hooks.ts b/scripts/ts-resolve-hooks.ts new file mode 100644 index 00000000..572fbf37 --- /dev/null +++ b/scripts/ts-resolve-hooks.ts @@ -0,0 +1,28 @@ +/** + * ESM resolve hook — rewrites .js specifiers to .ts when the .js file + * does not exist on disk. Needed because TypeScript's moduleResolution + * "nodenext" convention uses .js extensions in imports, but at runtime + * the source files are .ts. + * + * Loaded via module.register() from ts-resolve-loader.ts. + */ + +import { existsSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; + +export async function resolve( + specifier: string, + context: { parentURL?: string; conditions: string[] }, + nextResolve: (specifier: string, context?: { parentURL?: string; conditions: string[] }) => Promise<{ url: string; shortCircuit?: boolean }>, +): Promise<{ url: string; shortCircuit?: boolean }> { + try { + return await nextResolve(specifier, context); + } catch (err: any) { + // Only attempt .js → .ts fallback for file-relative specifiers + if (err?.code === 'ERR_MODULE_NOT_FOUND' && specifier.endsWith('.js')) { + const tsSpecifier = specifier.slice(0, -3) + '.ts'; + return nextResolve(tsSpecifier, context); + } + throw err; + } +} diff --git a/scripts/ts-resolve-loader.ts b/scripts/ts-resolve-loader.ts new file mode 100644 index 00000000..241c87ec --- /dev/null +++ b/scripts/ts-resolve-loader.ts @@ -0,0 +1,9 @@ +/** + * Registers the .js → .ts ESM resolve hook. + * + * Usage: node --experimental-strip-types --import ./scripts/ts-resolve-loader.ts src/cli.ts + */ + +import { register } from 'node:module'; + +register(new URL('./ts-resolve-hooks.ts', import.meta.url)); diff --git a/scripts/update-benchmark-report.js b/scripts/update-benchmark-report.ts similarity index 100% rename from scripts/update-benchmark-report.js rename to scripts/update-benchmark-report.ts diff --git a/scripts/update-embedding-report.js b/scripts/update-embedding-report.ts similarity index 100% rename from scripts/update-embedding-report.js rename to scripts/update-embedding-report.ts diff --git a/scripts/update-incremental-report.js b/scripts/update-incremental-report.ts similarity index 100% rename from scripts/update-incremental-report.js rename to scripts/update-incremental-report.ts diff --git a/scripts/update-query-report.js b/scripts/update-query-report.ts similarity index 100% rename from scripts/update-query-report.js rename to scripts/update-query-report.ts diff --git a/scripts/update-token-report.js b/scripts/update-token-report.ts similarity index 100% rename from scripts/update-token-report.js rename to scripts/update-token-report.ts diff --git a/scripts/verify-imports.js b/scripts/verify-imports.ts similarity index 100% rename from scripts/verify-imports.js rename to scripts/verify-imports.ts diff --git a/src/index.cjs b/src/index.cjs index 811e449c..2b06f4a6 100644 --- a/src/index.cjs +++ b/src/index.cjs @@ -13,4 +13,4 @@ // Note: if import() rejects (e.g. missing dependency), the rejected Promise is cached // by the CJS module system and every subsequent require() call will re-surface the same // rejection without re-attempting the load. -module.exports = import('./index.js'); +module.exports = import('./index.ts'); diff --git a/tests/benchmarks/resolution/resolution-benchmark.test.js b/tests/benchmarks/resolution/resolution-benchmark.test.ts similarity index 86% rename from tests/benchmarks/resolution/resolution-benchmark.test.js rename to tests/benchmarks/resolution/resolution-benchmark.test.ts index fa7c248d..599c60cc 100644 --- a/tests/benchmarks/resolution/resolution-benchmark.test.js +++ b/tests/benchmarks/resolution/resolution-benchmark.test.ts @@ -17,6 +17,42 @@ import { afterAll, beforeAll, describe, expect, test } from 'vitest'; import { openReadonlyOrFail } from '../../../src/db/index.js'; import { buildGraph } from '../../../src/domain/graph/builder.js'; +// ── Types ───────────────────────────────────────────────────────────────── + +interface ResolvedEdge { + source_name: string; + source_file: string; + target_name: string; + target_file: string; + kind: string; + confidence: number; +} + +interface ExpectedEdge { + source: { name: string; file: string }; + target: { name: string; file: string }; + mode?: string; +} + +interface ModeMetrics { + expected: number; + resolved: number; + recall?: number; +} + +interface BenchmarkMetrics { + precision: number; + recall: number; + truePositives: number; + falsePositives: number; + falseNegatives: number; + totalResolved: number; + totalExpected: number; + byMode: Record; + falsePositiveEdges: string[]; + falseNegativeEdges: string[]; +} + // ── Configuration ──────────────────────────────────────────────────────── const FIXTURES_DIR = path.join(import.meta.dirname, 'fixtures'); @@ -40,7 +76,7 @@ const THRESHOLDS = { * Copy fixture to a temp directory so buildGraph can write .codegraph/ without * polluting the repo. */ -function copyFixture(lang) { +function copyFixture(lang: string): string { const src = path.join(FIXTURES_DIR, lang); const tmp = fs.mkdtempSync(path.join(os.tmpdir(), `codegraph-resolution-${lang}-`)); for (const entry of fs.readdirSync(src, { withFileTypes: true })) { @@ -54,7 +90,7 @@ function copyFixture(lang) { /** * Build graph for a fixture directory. */ -async function buildFixtureGraph(fixtureDir) { +async function buildFixtureGraph(fixtureDir: string): Promise { await buildGraph(fixtureDir, { incremental: false, engine: 'wasm', @@ -68,7 +104,7 @@ async function buildFixtureGraph(fixtureDir) { * Extract all call edges from the built graph DB. * Returns array of { sourceName, sourceFile, targetName, targetFile, kind, confidence }. */ -function extractResolvedEdges(fixtureDir) { +function extractResolvedEdges(fixtureDir: string) { const dbPath = path.join(fixtureDir, '.codegraph', 'graph.db'); const db = openReadonlyOrFail(dbPath); try { @@ -97,14 +133,19 @@ function extractResolvedEdges(fixtureDir) { /** * Normalize a file path to just the basename for comparison. */ -function normalizeFile(filePath) { +function normalizeFile(filePath: string): string { return path.basename(filePath); } /** * Build a string key for an edge to enable set-based comparison. */ -function edgeKey(sourceName, sourceFile, targetName, targetFile) { +function edgeKey( + sourceName: string, + sourceFile: string, + targetName: string, + targetFile: string, +): string { return `${sourceName}@${normalizeFile(sourceFile)} -> ${targetName}@${normalizeFile(targetFile)}`; } @@ -112,7 +153,10 @@ function edgeKey(sourceName, sourceFile, targetName, targetFile) { * Compare resolved edges against expected edges manifest. * Returns precision, recall, and detailed breakdown by mode. */ -function computeMetrics(resolvedEdges, expectedEdges) { +function computeMetrics( + resolvedEdges: ResolvedEdge[], + expectedEdges: ExpectedEdge[], +): BenchmarkMetrics { // Build sets for overall comparison const resolvedSet = new Set( resolvedEdges.map((e) => edgeKey(e.source_name, e.source_file, e.target_name, e.target_file)), @@ -135,7 +179,7 @@ function computeMetrics(resolvedEdges, expectedEdges) { const recall = expectedSet.size > 0 ? truePositives.size / expectedSet.size : 0; // Break down by resolution mode - const byMode = {}; + const byMode: Record = {}; for (const edge of expectedEdges) { const mode = edge.mode || 'unknown'; if (!byMode[mode]) byMode[mode] = { expected: 0, resolved: 0 }; @@ -168,7 +212,7 @@ function computeMetrics(resolvedEdges, expectedEdges) { /** * Format a metrics report for console output. */ -function formatReport(lang, metrics) { +function formatReport(lang: string, metrics: BenchmarkMetrics): string { const lines = [ `\n ── ${lang.toUpperCase()} Resolution Metrics ──`, ` Precision: ${(metrics.precision * 100).toFixed(1)}% (${metrics.truePositives} correct / ${metrics.totalResolved} resolved)`, @@ -208,9 +252,9 @@ function formatReport(lang, metrics) { /** * Discover all fixture languages that have an expected-edges.json manifest. */ -function discoverFixtures() { +function discoverFixtures(): string[] { if (!fs.existsSync(FIXTURES_DIR)) return []; - const languages = []; + const languages: string[] = []; for (const dir of fs.readdirSync(FIXTURES_DIR)) { const manifestPath = path.join(FIXTURES_DIR, dir, 'expected-edges.json'); if (fs.existsSync(manifestPath)) { @@ -223,7 +267,7 @@ function discoverFixtures() { const languages = discoverFixtures(); /** Stores all results for the final summary */ -const allResults = {}; +const allResults: Record = {}; describe('Call Resolution Precision/Recall', () => { afterAll(() => { @@ -242,10 +286,10 @@ describe('Call Resolution Precision/Recall', () => { for (const lang of languages) { describe(lang, () => { - let fixtureDir; - let resolvedEdges; - let expectedEdges; - let metrics; + let fixtureDir: string; + let resolvedEdges: ResolvedEdge[]; + let expectedEdges: ExpectedEdge[]; + let metrics: BenchmarkMetrics; beforeAll(async () => { fixtureDir = copyFixture(lang); diff --git a/tests/builder/collect-files.test.js b/tests/builder/collect-files.test.ts similarity index 99% rename from tests/builder/collect-files.test.js rename to tests/builder/collect-files.test.ts index 4d4955c5..8cc0c937 100644 --- a/tests/builder/collect-files.test.js +++ b/tests/builder/collect-files.test.ts @@ -8,7 +8,7 @@ import { afterAll, beforeAll, describe, expect, it } from 'vitest'; import { PipelineContext } from '../../src/domain/graph/builder/context.js'; import { collectFiles } from '../../src/domain/graph/builder/stages/collect-files.js'; -let tmpDir; +let tmpDir: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-stage-collect-')); diff --git a/tests/builder/context.test.js b/tests/builder/context.test.ts similarity index 100% rename from tests/builder/context.test.js rename to tests/builder/context.test.ts diff --git a/tests/builder/detect-changes.test.js b/tests/builder/detect-changes.test.ts similarity index 99% rename from tests/builder/detect-changes.test.js rename to tests/builder/detect-changes.test.ts index 2555b097..0d13fd72 100644 --- a/tests/builder/detect-changes.test.js +++ b/tests/builder/detect-changes.test.ts @@ -10,7 +10,7 @@ import { PipelineContext } from '../../src/domain/graph/builder/context.js'; import { detectChanges } from '../../src/domain/graph/builder/stages/detect-changes.js'; import { writeJournalHeader } from '../../src/domain/graph/journal.js'; -let tmpDir; +let tmpDir: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-stage-detect-')); diff --git a/tests/builder/pipeline.test.js b/tests/builder/pipeline.test.ts similarity index 99% rename from tests/builder/pipeline.test.js rename to tests/builder/pipeline.test.ts index 76624d61..350808fb 100644 --- a/tests/builder/pipeline.test.js +++ b/tests/builder/pipeline.test.ts @@ -11,7 +11,7 @@ import { afterAll, beforeAll, describe, expect, it } from 'vitest'; import { buildGraph } from '../../src/domain/graph/builder/pipeline.js'; const FIXTURE_DIR = path.join(import.meta.dirname, '..', 'fixtures', 'sample-project'); -let tmpDir; +let tmpDir: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-pipeline-')); diff --git a/tests/engines/dataflow-parity.test.js b/tests/engines/dataflow-parity.test.ts similarity index 99% rename from tests/engines/dataflow-parity.test.js rename to tests/engines/dataflow-parity.test.ts index 0ed2d996..ce1c314d 100644 --- a/tests/engines/dataflow-parity.test.js +++ b/tests/engines/dataflow-parity.test.ts @@ -16,8 +16,8 @@ import { createParsers, getParser } from '../../src/domain/parser.js'; import { extractDataflow } from '../../src/features/dataflow.js'; import { isNativeAvailable } from '../../src/infrastructure/native.js'; -let native; -let parsers; +let native: any; +let parsers: any; let nativeHasDataflow = false; /** diff --git a/tests/engines/parity.test.js b/tests/engines/parity.test.ts similarity index 99% rename from tests/engines/parity.test.js rename to tests/engines/parity.test.ts index ef966af3..d90fd42d 100644 --- a/tests/engines/parity.test.js +++ b/tests/engines/parity.test.ts @@ -23,8 +23,8 @@ import { } from '../../src/domain/parser.js'; import { isNativeAvailable } from '../../src/infrastructure/native.js'; -let native; -let parsers; +let native: any; +let parsers: any; function wasmExtract(code, filePath) { const parser = getParser(parsers, filePath); diff --git a/tests/engines/query-walk-parity.test.js b/tests/engines/query-walk-parity.test.ts similarity index 99% rename from tests/engines/query-walk-parity.test.js rename to tests/engines/query-walk-parity.test.ts index 05335c5c..2b1f3e78 100644 --- a/tests/engines/query-walk-parity.test.js +++ b/tests/engines/query-walk-parity.test.ts @@ -12,7 +12,7 @@ import { beforeAll, describe, expect, it } from 'vitest'; import { createParsers, getParser, parseFileAuto } from '../../src/domain/parser.js'; import { extractSymbols } from '../../src/extractors/javascript.js'; -let parsers; +let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/graph/algorithms/bfs.test.js b/tests/graph/algorithms/bfs.test.ts similarity index 100% rename from tests/graph/algorithms/bfs.test.js rename to tests/graph/algorithms/bfs.test.ts diff --git a/tests/graph/algorithms/centrality.test.js b/tests/graph/algorithms/centrality.test.ts similarity index 100% rename from tests/graph/algorithms/centrality.test.js rename to tests/graph/algorithms/centrality.test.ts diff --git a/tests/graph/algorithms/leiden.test.js b/tests/graph/algorithms/leiden.test.ts similarity index 100% rename from tests/graph/algorithms/leiden.test.js rename to tests/graph/algorithms/leiden.test.ts diff --git a/tests/graph/algorithms/louvain.test.js b/tests/graph/algorithms/louvain.test.ts similarity index 100% rename from tests/graph/algorithms/louvain.test.js rename to tests/graph/algorithms/louvain.test.ts diff --git a/tests/graph/algorithms/shortest-path.test.js b/tests/graph/algorithms/shortest-path.test.ts similarity index 100% rename from tests/graph/algorithms/shortest-path.test.js rename to tests/graph/algorithms/shortest-path.test.ts diff --git a/tests/graph/algorithms/tarjan.test.js b/tests/graph/algorithms/tarjan.test.ts similarity index 100% rename from tests/graph/algorithms/tarjan.test.js rename to tests/graph/algorithms/tarjan.test.ts diff --git a/tests/graph/builders/dependency.test.js b/tests/graph/builders/dependency.test.ts similarity index 100% rename from tests/graph/builders/dependency.test.js rename to tests/graph/builders/dependency.test.ts diff --git a/tests/graph/builders/structure.test.js b/tests/graph/builders/structure.test.ts similarity index 100% rename from tests/graph/builders/structure.test.js rename to tests/graph/builders/structure.test.ts diff --git a/tests/graph/builders/temporal.test.js b/tests/graph/builders/temporal.test.ts similarity index 100% rename from tests/graph/builders/temporal.test.js rename to tests/graph/builders/temporal.test.ts diff --git a/tests/graph/classifiers/risk.test.js b/tests/graph/classifiers/risk.test.ts similarity index 100% rename from tests/graph/classifiers/risk.test.js rename to tests/graph/classifiers/risk.test.ts diff --git a/tests/graph/classifiers/roles.test.js b/tests/graph/classifiers/roles.test.ts similarity index 100% rename from tests/graph/classifiers/roles.test.js rename to tests/graph/classifiers/roles.test.ts diff --git a/tests/graph/cycles.test.js b/tests/graph/cycles.test.ts similarity index 100% rename from tests/graph/cycles.test.js rename to tests/graph/cycles.test.ts diff --git a/tests/graph/export.test.js b/tests/graph/export.test.ts similarity index 100% rename from tests/graph/export.test.js rename to tests/graph/export.test.ts diff --git a/tests/graph/model.test.js b/tests/graph/model.test.ts similarity index 100% rename from tests/graph/model.test.js rename to tests/graph/model.test.ts diff --git a/tests/graph/viewer.test.js b/tests/graph/viewer.test.ts similarity index 100% rename from tests/graph/viewer.test.js rename to tests/graph/viewer.test.ts diff --git a/tests/helpers/fixtures.js b/tests/helpers/fixtures.ts similarity index 58% rename from tests/helpers/fixtures.js rename to tests/helpers/fixtures.ts index e9225b12..33154720 100644 --- a/tests/helpers/fixtures.js +++ b/tests/helpers/fixtures.ts @@ -10,84 +10,89 @@ import { InMemoryRepository } from '../../src/db/repository/in-memory-repository * .calls('authMiddleware', 'authenticate') * .build(); */ -class TestRepoBuilder { - #pending = { nodes: [], edges: [], complexity: [] }; +interface PendingNode { + name: string; + kind: string; + file: string; + line: number; + [key: string]: unknown; +} - /** - * Add a function node. - * @param {string} name - * @param {string} file - * @param {number} line - * @param {object} [extra] - Additional node attrs (role, end_line, scope, etc.) - */ - fn(name, file, line, extra = {}) { +interface PendingEdge { + source: string; + target: string; + kind: string; +} + +interface PendingComplexity { + name: string; + metrics: Record; +} + +class TestRepoBuilder { + #pending: { nodes: PendingNode[]; edges: PendingEdge[]; complexity: PendingComplexity[] } = { + nodes: [], + edges: [], + complexity: [], + }; + + /** Add a function node. */ + fn(name: string, file: string, line: number, extra: Record = {}): this { return this.#addNode(name, 'function', file, line, extra); } - /** - * Add a method node. - */ - method(name, file, line, extra = {}) { + /** Add a method node. */ + method(name: string, file: string, line: number, extra: Record = {}): this { return this.#addNode(name, 'method', file, line, extra); } - /** - * Add a class node. - */ - cls(name, file, line, extra = {}) { + /** Add a class node. */ + cls(name: string, file: string, line: number, extra: Record = {}): this { return this.#addNode(name, 'class', file, line, extra); } - /** - * Add a file node. - */ - file(filePath) { + /** Add a file node. */ + file(filePath: string): this { return this.#addNode(filePath, 'file', filePath, 0); } - /** - * Add an arbitrary node. - */ - node(name, kind, file, line, extra = {}) { + /** Add an arbitrary node. */ + node( + name: string, + kind: string, + file: string, + line: number, + extra: Record = {}, + ): this { return this.#addNode(name, kind, file, line, extra); } - /** - * Add a 'calls' edge between two named nodes. - */ - calls(sourceName, targetName) { + /** Add a 'calls' edge between two named nodes. */ + calls(sourceName: string, targetName: string): this { this.#pending.edges.push({ source: sourceName, target: targetName, kind: 'calls' }); return this; } - /** - * Add an 'imports' edge. - */ - imports(sourceName, targetName) { + /** Add an 'imports' edge. */ + imports(sourceName: string, targetName: string): this { this.#pending.edges.push({ source: sourceName, target: targetName, kind: 'imports' }); return this; } - /** - * Add an 'extends' edge. - */ - extends(sourceName, targetName) { + /** Add an 'extends' edge. */ + extends(sourceName: string, targetName: string): this { this.#pending.edges.push({ source: sourceName, target: targetName, kind: 'extends' }); return this; } - /** - * Add an edge of any kind. - */ - edge(sourceName, targetName, kind) { + /** Add an edge of any kind. */ + edge(sourceName: string, targetName: string, kind: string): this { this.#pending.edges.push({ source: sourceName, target: targetName, kind }); return this; } - /** - * Add complexity metrics for a named node. - */ - complexity(name, metrics) { + /** Add complexity metrics for a named node. */ + complexity(name: string, metrics: Record): this { this.#pending.complexity.push({ name, metrics }); return this; } @@ -129,16 +134,19 @@ class TestRepoBuilder { return { repo, ids }; } - #addNode(name, kind, file, line, extra = {}) { + #addNode( + name: string, + kind: string, + file: string, + line: number, + extra: Record = {}, + ): this { this.#pending.nodes.push({ name, kind, file, line, ...extra }); return this; } } -/** - * Create a new TestRepoBuilder. - * @returns {TestRepoBuilder} - */ -export function createTestRepo() { +/** Create a new TestRepoBuilder. */ +export function createTestRepo(): TestRepoBuilder { return new TestRepoBuilder(); } diff --git a/tests/helpers/node-version.js b/tests/helpers/node-version.js deleted file mode 100644 index f8603a82..00000000 --- a/tests/helpers/node-version.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Node >= 22.6 supports --experimental-strip-types, required for tests that - * spawn child processes loading .ts source files directly. - */ -const [major, minor] = process.versions.node.split('.').map(Number); -export const canStripTypes = major > 22 || (major === 22 && minor >= 6); diff --git a/tests/incremental/cache.test.js b/tests/incremental/cache.test.ts similarity index 99% rename from tests/incremental/cache.test.js rename to tests/incremental/cache.test.ts index 46ee6290..0cfc2b92 100644 --- a/tests/incremental/cache.test.js +++ b/tests/incremental/cache.test.ts @@ -10,7 +10,7 @@ import { isNativeAvailable, loadNative } from '../../src/infrastructure/native.j const hasNative = isNativeAvailable(); describe.skipIf(!hasNative)('ParseTreeCache', () => { - let cache; + let cache: any; beforeEach(() => { const native = loadNative(); diff --git a/tests/incremental/watcher-incremental.test.js b/tests/incremental/watcher-incremental.test.ts similarity index 98% rename from tests/incremental/watcher-incremental.test.js rename to tests/incremental/watcher-incremental.test.ts index 8c273203..e8220d5d 100644 --- a/tests/incremental/watcher-incremental.test.js +++ b/tests/incremental/watcher-incremental.test.ts @@ -17,8 +17,8 @@ import { isNativeAvailable } from '../../src/infrastructure/native.js'; const hasNative = isNativeAvailable(); describe.skipIf(!hasNative)('Watcher incremental flow', () => { - let tmpDir; - let cache; + let tmpDir: string; + let cache: any; beforeEach(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-inc-')); diff --git a/tests/integration/ast.test.js b/tests/integration/ast.test.ts similarity index 99% rename from tests/integration/ast.test.js rename to tests/integration/ast.test.ts index 706684d9..8c731322 100644 --- a/tests/integration/ast.test.js +++ b/tests/integration/ast.test.ts @@ -30,7 +30,7 @@ function insertAstNode(db, file, line, kind, name, text, receiver, parentNodeId) // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-ast-')); diff --git a/tests/integration/audit.test.js b/tests/integration/audit.test.ts similarity index 99% rename from tests/integration/audit.test.js rename to tests/integration/audit.test.ts index f6d00768..547bb32e 100644 --- a/tests/integration/audit.test.js +++ b/tests/integration/audit.test.ts @@ -46,7 +46,7 @@ function insertComplexity(db, nodeId, opts = {}) { // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-audit-')); diff --git a/tests/integration/batch.test.js b/tests/integration/batch.test.ts similarity index 95% rename from tests/integration/batch.test.js rename to tests/integration/batch.test.ts index 216c98e3..5fbb2172 100644 --- a/tests/integration/batch.test.js +++ b/tests/integration/batch.test.ts @@ -25,10 +25,6 @@ import { splitTargets, } from '../../src/features/batch.js'; -// Child processes load .ts files natively — requires Node >= 22.6 type stripping -const [_major, _minor] = process.versions.node.split('.').map(Number); -const canStripTypes = _major > 22 || (_major === 22 && _minor >= 6); - // ─── Helpers ─────────────────────────────────────────────────────────── function insertNode(db, name, kind, file, line) { @@ -45,7 +41,7 @@ function insertEdge(db, sourceId, targetId, kind = 'calls', confidence = 1.0) { // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-batch-')); @@ -212,17 +208,23 @@ describe('batchData — complexity (dbOnly signature)', () => { // ─── CLI smoke test ────────────────────────────────────────────────── -describe.skipIf(!canStripTypes)('batch CLI', () => { +describe('batch CLI', () => { const cliPath = path.resolve( path.dirname(new URL(import.meta.url).pathname.replace(/^\/([A-Z]:)/i, '$1')), - '../../src/cli.js', + '../../src/cli.ts', ); + const loaderUrl = new URL('../../scripts/ts-resolve-loader.ts', import.meta.url).href; + const NODE_TS_FLAGS = ['--experimental-strip-types', '--import', loaderUrl]; test('outputs valid JSON', () => { - const out = execFileSync('node', [cliPath, 'batch', 'query', 'authenticate', '--db', dbPath], { - encoding: 'utf-8', - timeout: 30_000, - }); + const out = execFileSync( + 'node', + [...NODE_TS_FLAGS, cliPath, 'batch', 'query', 'authenticate', '--db', dbPath], + { + encoding: 'utf-8', + timeout: 30_000, + }, + ); const parsed = JSON.parse(out); expect(parsed.command).toBe('query'); expect(parsed.total).toBe(1); @@ -232,7 +234,7 @@ describe.skipIf(!canStripTypes)('batch CLI', () => { test('batch accepts comma-separated positional targets', () => { const out = execFileSync( 'node', - [cliPath, 'batch', 'where', 'authenticate,validateToken', '--db', dbPath], + [...NODE_TS_FLAGS, cliPath, 'batch', 'where', 'authenticate,validateToken', '--db', dbPath], { encoding: 'utf-8', timeout: 30_000 }, ); const parsed = JSON.parse(out); diff --git a/tests/integration/branch-compare.test.js b/tests/integration/branch-compare.test.ts similarity index 99% rename from tests/integration/branch-compare.test.js rename to tests/integration/branch-compare.test.ts index 0a276117..b67326ae 100644 --- a/tests/integration/branch-compare.test.js +++ b/tests/integration/branch-compare.test.ts @@ -12,7 +12,7 @@ import path from 'node:path'; import { afterAll, beforeAll, describe, expect, test } from 'vitest'; import { branchCompareData, branchCompareMermaid } from '../../src/features/branch-compare.js'; -let tmpDir; +let tmpDir: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-bc-test-')); diff --git a/tests/integration/build-parity.test.js b/tests/integration/build-parity.test.ts similarity index 98% rename from tests/integration/build-parity.test.js rename to tests/integration/build-parity.test.ts index 98ce843d..a059d452 100644 --- a/tests/integration/build-parity.test.js +++ b/tests/integration/build-parity.test.ts @@ -61,8 +61,8 @@ function readGraph(dbPath) { } describeOrSkip('Build parity: native vs WASM', () => { - let wasmDir; - let nativeDir; + let wasmDir: string; + let nativeDir: string; beforeAll(async () => { // Create two temp copies of the fixture diff --git a/tests/integration/build.test.js b/tests/integration/build.test.ts similarity index 98% rename from tests/integration/build.test.js rename to tests/integration/build.test.ts index cfa1ebac..8ced90fe 100644 --- a/tests/integration/build.test.js +++ b/tests/integration/build.test.ts @@ -39,7 +39,7 @@ export function main() { `.trimStart(), }; -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(async () => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-build-')); @@ -154,7 +154,7 @@ describe('buildGraph', () => { }); describe('three-tier incremental builds', () => { - let incrDir, incrDbPath; + let incrDir: string, incrDbPath: string; beforeAll(async () => { incrDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-tier-')); @@ -361,7 +361,7 @@ describe('three-tier incremental builds', () => { }); describe('nested function caller attribution', () => { - let nestDir, nestDbPath; + let nestDir: string, nestDbPath: string; beforeAll(async () => { nestDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-nested-')); @@ -406,7 +406,7 @@ describe('nested function caller attribution', () => { }); describe('version/engine mismatch auto-promotes to full rebuild', () => { - let promoDir, promoDbPath; + let promoDir: string, promoDbPath: string; beforeAll(async () => { promoDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-promo-')); @@ -480,7 +480,7 @@ describe('version/engine mismatch auto-promotes to full rebuild', () => { }); describe('typed method call resolution', () => { - let typedDir, typedDbPath; + let typedDir: string, typedDbPath: string; beforeAll(async () => { typedDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-typed-')); diff --git a/tests/integration/cfg.test.js b/tests/integration/cfg.test.ts similarity index 99% rename from tests/integration/cfg.test.js rename to tests/integration/cfg.test.ts index 548042ea..33284ce6 100644 --- a/tests/integration/cfg.test.js +++ b/tests/integration/cfg.test.ts @@ -36,7 +36,7 @@ function insertEdge(db, fnNodeId, sourceBlockId, targetBlockId, kind) { // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-cfg-')); diff --git a/tests/integration/check.test.js b/tests/integration/check.test.ts similarity index 99% rename from tests/integration/check.test.js rename to tests/integration/check.test.ts index 1e339188..9866f20e 100644 --- a/tests/integration/check.test.js +++ b/tests/integration/check.test.ts @@ -36,7 +36,7 @@ function insertEdge(db, sourceId, targetId, kind) { // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath, db; +let tmpDir: string, dbPath: string, db: any; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-check-')); diff --git a/tests/integration/cli.test.js b/tests/integration/cli.test.ts similarity index 95% rename from tests/integration/cli.test.js rename to tests/integration/cli.test.ts index 52cc8ff1..d254a578 100644 --- a/tests/integration/cli.test.js +++ b/tests/integration/cli.test.ts @@ -9,11 +9,9 @@ import os from 'node:os'; import path from 'node:path'; import { afterAll, beforeAll, describe, expect, test } from 'vitest'; -// All tests spawn child processes that load .ts files — requires Node >= 22.6 -const [_major, _minor] = process.versions.node.split('.').map(Number); -const canStripTypes = _major > 22 || (_major === 22 && _minor >= 6); - -const CLI = path.resolve('src/cli.js'); +const CLI = path.resolve('src/cli.ts'); +const LOADER = new URL('../../scripts/ts-resolve-loader.ts', import.meta.url).href; +const NODE_TS_FLAGS = ['--experimental-strip-types', '--import', LOADER]; const FIXTURE_FILES = { 'math.js': ` @@ -40,11 +38,11 @@ export function main() { `.trimStart(), }; -let tmpDir, tmpHome, dbPath; +let tmpDir: string, tmpHome: string, dbPath: string; /** Run the CLI and return stdout as a string. Throws on non-zero exit. */ function run(...args) { - return execFileSync('node', [CLI, ...args], { + return execFileSync('node', [...NODE_TS_FLAGS, CLI, ...args], { cwd: tmpDir, encoding: 'utf-8', timeout: 30_000, @@ -69,7 +67,7 @@ afterAll(() => { if (tmpHome) fs.rmSync(tmpHome, { recursive: true, force: true }); }); -describe.skipIf(!canStripTypes)('CLI smoke tests', () => { +describe('CLI smoke tests', () => { // ─── Build ─────────────────────────────────────────────────────────── test('build creates graph.db', () => { expect(fs.existsSync(dbPath)).toBe(true); @@ -200,7 +198,7 @@ describe.skipIf(!canStripTypes)('CLI smoke tests', () => { const { spawnSync } = require('node:child_process'); const result = spawnSync( 'node', - [CLI, 'query', 'sumOfSquares', '--path', 'add', '--db', dbPath, '--json'], + [...NODE_TS_FLAGS, CLI, 'query', 'sumOfSquares', '--path', 'add', '--db', dbPath, '--json'], { cwd: tmpDir, encoding: 'utf-8', @@ -241,12 +239,12 @@ describe.skipIf(!canStripTypes)('CLI smoke tests', () => { // ─── Registry CLI ─────────────────────────────────────────────────────── -describe.skipIf(!canStripTypes)('Registry CLI commands', () => { - let tmpHome; +describe('Registry CLI commands', () => { + let tmpHome: string; /** Run CLI with isolated HOME to avoid touching real registry */ function runReg(...args) { - return execFileSync('node', [CLI, ...args], { + return execFileSync('node', [...NODE_TS_FLAGS, CLI, ...args], { cwd: tmpDir, encoding: 'utf-8', timeout: 30_000, diff --git a/tests/integration/cochange.test.js b/tests/integration/cochange.test.ts similarity index 99% rename from tests/integration/cochange.test.js rename to tests/integration/cochange.test.ts index 5dbed8fb..e33dc9d8 100644 --- a/tests/integration/cochange.test.js +++ b/tests/integration/cochange.test.ts @@ -111,7 +111,7 @@ describe('computeCoChanges', () => { // ─── B. DB integration (coChangeData / coChangeTopData) ────────────── describe('coChangeData + coChangeTopData', () => { - let tmpDir, dbPath; + let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-cochange-')); @@ -207,7 +207,7 @@ describe('coChangeData + coChangeTopData', () => { // ─── C. scanGitHistory (real git repo) ─────────────────────────────── describe('scanGitHistory', () => { - let tmpDir; + let tmpDir: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-git-')); diff --git a/tests/integration/communities.test.js b/tests/integration/communities.test.ts similarity index 99% rename from tests/integration/communities.test.js rename to tests/integration/communities.test.ts index 90324367..b48df12f 100644 --- a/tests/integration/communities.test.js +++ b/tests/integration/communities.test.ts @@ -16,7 +16,7 @@ import { createTestRepo } from '../helpers/fixtures.js'; // ─── Fixture ────────────────────────────────────────────────────────── -let repo; +let repo: any; beforeAll(() => { ({ repo } = createTestRepo() diff --git a/tests/integration/complexity.test.js b/tests/integration/complexity.test.ts similarity index 99% rename from tests/integration/complexity.test.js rename to tests/integration/complexity.test.ts index 8d7ea175..ddfbd8a8 100644 --- a/tests/integration/complexity.test.js +++ b/tests/integration/complexity.test.ts @@ -76,7 +76,7 @@ function insertComplexity( // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-complexity-')); diff --git a/tests/integration/context.test.js b/tests/integration/context.test.ts similarity index 99% rename from tests/integration/context.test.js rename to tests/integration/context.test.ts index 14e39b05..1ee3e91e 100644 --- a/tests/integration/context.test.js +++ b/tests/integration/context.test.ts @@ -29,7 +29,7 @@ function insertEdge(db, sourceId, targetId, kind) { // ─── Fixture ────────────────────────────────────────────────────────── -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-context-')); diff --git a/tests/integration/dataflow.test.js b/tests/integration/dataflow.test.ts similarity index 99% rename from tests/integration/dataflow.test.js rename to tests/integration/dataflow.test.ts index 78eb679d..29bd7c95 100644 --- a/tests/integration/dataflow.test.js +++ b/tests/integration/dataflow.test.ts @@ -43,7 +43,7 @@ function insertDataflow(db, sourceId, targetId, kind, opts = {}) { // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-dataflow-')); @@ -300,7 +300,7 @@ describe('dataflowImpactData', () => { // ─── Empty dataflow table ────────────────────────────────────────────── describe('empty dataflow', () => { - let emptyDbPath; + let emptyDbPath: string; beforeAll(() => { const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-df-empty-')); diff --git a/tests/integration/exports.test.js b/tests/integration/exports.test.ts similarity index 99% rename from tests/integration/exports.test.js rename to tests/integration/exports.test.ts index 390bf7c1..46762128 100644 --- a/tests/integration/exports.test.js +++ b/tests/integration/exports.test.ts @@ -47,7 +47,7 @@ function insertEdge(db, sourceId, targetId, kind, confidence = 1.0) { // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-exports-')); diff --git a/tests/integration/flow.test.js b/tests/integration/flow.test.ts similarity index 99% rename from tests/integration/flow.test.js rename to tests/integration/flow.test.ts index fc4ca7aa..8e1eba6f 100644 --- a/tests/integration/flow.test.js +++ b/tests/integration/flow.test.ts @@ -37,7 +37,7 @@ function insertEdge(db, sourceId, targetId, kind, confidence = 1.0) { // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-flow-')); diff --git a/tests/integration/implementations.test.js b/tests/integration/implementations.test.ts similarity index 99% rename from tests/integration/implementations.test.js rename to tests/integration/implementations.test.ts index bdd56389..c4945dd3 100644 --- a/tests/integration/implementations.test.js +++ b/tests/integration/implementations.test.ts @@ -48,7 +48,7 @@ function insertEdge(db, sourceId, targetId, kind, confidence = 1.0) { // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-impl-')); diff --git a/tests/integration/incr-edge-gap.test.js b/tests/integration/incr-edge-gap.test.ts similarity index 100% rename from tests/integration/incr-edge-gap.test.js rename to tests/integration/incr-edge-gap.test.ts diff --git a/tests/integration/incremental-edge-parity.test.js b/tests/integration/incremental-edge-parity.test.ts similarity index 99% rename from tests/integration/incremental-edge-parity.test.js rename to tests/integration/incremental-edge-parity.test.ts index 0d87dbea..06e097f4 100644 --- a/tests/integration/incremental-edge-parity.test.js +++ b/tests/integration/incremental-edge-parity.test.ts @@ -102,7 +102,7 @@ async function buildAndCompare(fixtureDir, mutate) { describe('Incremental edge parity (CI gate)', () => { // Scenario 1: Comment-only touch — edges must be identical describe('comment-only touch', () => { - let result; + let result: any; beforeAll(async () => { result = await buildAndCompare(FIXTURE_DIR, (dir) => { @@ -122,7 +122,7 @@ describe('Incremental edge parity (CI gate)', () => { // Scenario 2: Body edit — change function implementation, keep exports describe('body edit (same exports)', () => { - let result; + let result: any; beforeAll(async () => { result = await buildAndCompare(FIXTURE_DIR, (dir) => { @@ -147,7 +147,7 @@ describe('Incremental edge parity (CI gate)', () => { // Scenario 3: New export added — edges from consumers should resolve describe('new export added', () => { - let result; + let result: any; beforeAll(async () => { result = await buildAndCompare(FIXTURE_DIR, (dir) => { @@ -206,7 +206,7 @@ describe('Incremental edge parity (CI gate)', () => { // Scenario 4: File deletion — stale edges must be purged describe('file deletion', () => { - let result; + let result: any; beforeAll(async () => { result = await buildAndCompare(FIXTURE_DIR, (dir) => { diff --git a/tests/integration/incremental-parity.test.js b/tests/integration/incremental-parity.test.ts similarity index 98% rename from tests/integration/incremental-parity.test.js rename to tests/integration/incremental-parity.test.ts index 21a2a56c..d632503e 100644 --- a/tests/integration/incremental-parity.test.js +++ b/tests/integration/incremental-parity.test.ts @@ -87,9 +87,9 @@ function readAnalysisTables(dbPath) { } describe('Incremental build parity: full vs incremental', () => { - let fullDir; - let incrDir; - let tmpBase; + let fullDir: string; + let incrDir: string; + let tmpBase: string; beforeAll(async () => { tmpBase = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-incr-parity-')); diff --git a/tests/integration/manifesto.test.js b/tests/integration/manifesto.test.ts similarity index 99% rename from tests/integration/manifesto.test.js rename to tests/integration/manifesto.test.ts index d9b5d93f..73691e9a 100644 --- a/tests/integration/manifesto.test.js +++ b/tests/integration/manifesto.test.ts @@ -52,7 +52,7 @@ function insertEdge(db, sourceId, targetId, kind, confidence = 1.0) { // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-manifesto-')); diff --git a/tests/integration/owners.test.js b/tests/integration/owners.test.ts similarity index 99% rename from tests/integration/owners.test.js rename to tests/integration/owners.test.ts index 7d26f958..025e1e1c 100644 --- a/tests/integration/owners.test.js +++ b/tests/integration/owners.test.ts @@ -22,7 +22,7 @@ function insertEdge(db, sourceId, targetId, kind, confidence = 1.0) { // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-owners-')); diff --git a/tests/integration/pagination.test.js b/tests/integration/pagination.test.ts similarity index 99% rename from tests/integration/pagination.test.js rename to tests/integration/pagination.test.ts index ecf31c5d..4bf5d0b0 100644 --- a/tests/integration/pagination.test.js +++ b/tests/integration/pagination.test.ts @@ -59,7 +59,7 @@ function insertEdge(db, sourceId, targetId, kind, confidence = 1.0) { // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath, dbForExport; +let tmpDir: string, dbPath: string, dbForExport: any; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-pagination-')); diff --git a/tests/integration/qualified-names.test.js b/tests/integration/qualified-names.test.ts similarity index 99% rename from tests/integration/qualified-names.test.js rename to tests/integration/qualified-names.test.ts index 3fc1beae..61b5eeb9 100644 --- a/tests/integration/qualified-names.test.js +++ b/tests/integration/qualified-names.test.ts @@ -49,8 +49,8 @@ export class MathUtils { `, }; -let tmpDir; -let dbPath; +let tmpDir: string; +let dbPath: string; beforeAll(async () => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-qualified-')); diff --git a/tests/integration/queries.test.js b/tests/integration/queries.test.ts similarity index 99% rename from tests/integration/queries.test.js rename to tests/integration/queries.test.ts index d4f7f2b2..6381cdb2 100644 --- a/tests/integration/queries.test.js +++ b/tests/integration/queries.test.ts @@ -58,7 +58,7 @@ function insertEdge(db, sourceId, targetId, kind, confidence = 1.0) { // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-queries-')); diff --git a/tests/integration/roles.test.js b/tests/integration/roles.test.ts similarity index 99% rename from tests/integration/roles.test.js rename to tests/integration/roles.test.ts index 9fce5e84..9a6f29da 100644 --- a/tests/integration/roles.test.js +++ b/tests/integration/roles.test.ts @@ -38,7 +38,7 @@ function insertEdge(db, sourceId, targetId, kind, confidence = 1.0) { // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-roles-')); diff --git a/tests/integration/scoped-rebuild.test.js b/tests/integration/scoped-rebuild.test.ts similarity index 99% rename from tests/integration/scoped-rebuild.test.js rename to tests/integration/scoped-rebuild.test.ts index 5f6eaa9a..b3ff350d 100644 --- a/tests/integration/scoped-rebuild.test.js +++ b/tests/integration/scoped-rebuild.test.ts @@ -14,7 +14,7 @@ import { buildGraph } from '../../src/domain/graph/builder.js'; const FIXTURE_DIR = path.join(import.meta.dirname, '..', 'fixtures', 'sample-project'); -let tmpDir; +let tmpDir: string; function copyFixture() { const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-scoped-')); diff --git a/tests/integration/sequence.test.js b/tests/integration/sequence.test.ts similarity index 99% rename from tests/integration/sequence.test.js rename to tests/integration/sequence.test.ts index e8705820..e4f01338 100644 --- a/tests/integration/sequence.test.js +++ b/tests/integration/sequence.test.ts @@ -41,7 +41,7 @@ function insertEdge(db, sourceId, targetId, kind, confidence = 1.0) { // ─── InMemory Fixture ───────────────────────────────────────────────── -let repo; +let repo: any; beforeAll(() => { ({ repo } = createTestRepo() @@ -199,7 +199,7 @@ describe('sequenceToMermaid', () => { // ─── Dataflow annotations (SQLite — requires dataflow table) ────────── describe('dataflow annotations', () => { - let dfTmpDir, dfDbPath; + let dfTmpDir: string, dfDbPath: string; beforeAll(() => { dfTmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-seq-df-')); diff --git a/tests/integration/structure.test.js b/tests/integration/structure.test.ts similarity index 99% rename from tests/integration/structure.test.js rename to tests/integration/structure.test.ts index fe97a118..762918e4 100644 --- a/tests/integration/structure.test.js +++ b/tests/integration/structure.test.ts @@ -36,7 +36,7 @@ export function main() { printSum(1, double(2)); } `.trimStart(), }; -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(async () => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-structure-')); diff --git a/tests/integration/triage.test.js b/tests/integration/triage.test.ts similarity index 99% rename from tests/integration/triage.test.js rename to tests/integration/triage.test.ts index 32a2b692..e7bdcb86 100644 --- a/tests/integration/triage.test.js +++ b/tests/integration/triage.test.ts @@ -10,7 +10,7 @@ import { createTestRepo } from '../helpers/fixtures.js'; // ─── Fixture ────────────────────────────────────────────────────────── -let repo; +let repo: any; beforeAll(() => { const builder = createTestRepo() diff --git a/tests/integration/watcher-rebuild.test.js b/tests/integration/watcher-rebuild.test.ts similarity index 98% rename from tests/integration/watcher-rebuild.test.js rename to tests/integration/watcher-rebuild.test.ts index b2f03142..be394f71 100644 --- a/tests/integration/watcher-rebuild.test.js +++ b/tests/integration/watcher-rebuild.test.ts @@ -92,9 +92,9 @@ function makeStmts(db) { } describe('Watcher rebuildFile parity (#533)', () => { - let fullDir; - let watcherDir; - let tmpBase; + let fullDir: string; + let watcherDir: string; + let tmpBase: string; beforeAll(async () => { tmpBase = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-watcher-533-')); diff --git a/tests/parsers/ast-all-langs.test.js b/tests/parsers/ast-all-langs.test.ts similarity index 99% rename from tests/parsers/ast-all-langs.test.js rename to tests/parsers/ast-all-langs.test.ts index b4130a18..28e2d2b7 100644 --- a/tests/parsers/ast-all-langs.test.js +++ b/tests/parsers/ast-all-langs.test.ts @@ -40,7 +40,7 @@ function queryAll(db) { // ─── JS-side: buildAstNodes accepts astNodes for non-JS files ──────── describe('buildAstNodes — non-JS language astNodes', () => { - let tmpDir, db; + let tmpDir: string, db: any; beforeAll(() => { ({ tmpDir, db } = createTempDb()); @@ -266,7 +266,7 @@ function nativeSupportsMultiLangAst() { const canTestMultiLang = nativeSupportsMultiLangAst(); describe.skipIf(!canTestMultiLang)('native AST nodes — multi-language', () => { - let tmpDir, db; + let tmpDir: string, db: any; beforeAll(async () => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-ast-multilang-')); diff --git a/tests/parsers/ast-nodes.test.js b/tests/parsers/ast-nodes.test.ts similarity index 99% rename from tests/parsers/ast-nodes.test.js rename to tests/parsers/ast-nodes.test.ts index 3d3c3de7..5f3e9179 100644 --- a/tests/parsers/ast-nodes.test.js +++ b/tests/parsers/ast-nodes.test.ts @@ -47,7 +47,7 @@ function helper() { // ─── Setup ──────────────────────────────────────────────────────────── -let tmpDir, dbPath, db; +let tmpDir: string, dbPath: string, db: any; beforeAll(async () => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-ast-extract-')); @@ -210,7 +210,7 @@ function nativeSupportsAstNodes() { const canTestNative = nativeSupportsAstNodes(); describe.skipIf(!canTestNative)('buildAstNodes — native engine', () => { - let nativeTmpDir, nativeDbPath, nativeDb; + let nativeTmpDir: string, nativeDbPath: string, nativeDb: any; function queryNativeAstNodes(kind) { return nativeDb.prepare('SELECT * FROM ast_nodes WHERE kind = ? ORDER BY line').all(kind); diff --git a/tests/parsers/cfg-all-langs.test.js b/tests/parsers/cfg-all-langs.test.ts similarity index 99% rename from tests/parsers/cfg-all-langs.test.js rename to tests/parsers/cfg-all-langs.test.ts index f37c9a95..b13308c8 100644 --- a/tests/parsers/cfg-all-langs.test.js +++ b/tests/parsers/cfg-all-langs.test.ts @@ -34,7 +34,7 @@ function createTempDb() { // ─── JS-side: buildCFGData accepts native def.cfg ───────────────────── describe('buildCFGData — native CFG path', () => { - let tmpDir, db; + let tmpDir: string, db: any; beforeAll(() => { ({ tmpDir, db } = createTempDb()); @@ -294,7 +294,7 @@ const hasFixedCfg = (() => { })(); describe.skipIf(!canTestNativeCfg)('native CFG — multi-language', () => { - let tmpDir; + let tmpDir: string; const nativeResults = new Map(); beforeAll(async () => { @@ -389,9 +389,9 @@ describe.skipIf(!canTestNativeCfg)('native CFG — multi-language', () => { // ─── Parity: native vs WASM CFG ────────────────────────────────────── describe.skipIf(!canTestNativeCfg || !hasFixedCfg)('native vs WASM CFG parity', () => { - let tmpDir; + let tmpDir: string; const nativeResults = new Map(); - let parsers; + let parsers: any; const LANG_MAP = { '.js': 'javascript', diff --git a/tests/parsers/csharp.test.js b/tests/parsers/csharp.test.ts similarity index 99% rename from tests/parsers/csharp.test.js rename to tests/parsers/csharp.test.ts index 46ccd25c..8f9f4e0c 100644 --- a/tests/parsers/csharp.test.js +++ b/tests/parsers/csharp.test.ts @@ -2,7 +2,7 @@ import { beforeAll, describe, expect, it } from 'vitest'; import { createParsers, extractCSharpSymbols } from '../../src/domain/parser.js'; describe('C# parser', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/dataflow-csharp.test.js b/tests/parsers/dataflow-csharp.test.ts similarity index 99% rename from tests/parsers/dataflow-csharp.test.js rename to tests/parsers/dataflow-csharp.test.ts index 30f91a2d..613438e1 100644 --- a/tests/parsers/dataflow-csharp.test.js +++ b/tests/parsers/dataflow-csharp.test.ts @@ -6,7 +6,7 @@ import { createParsers } from '../../src/domain/parser.js'; import { extractDataflow } from '../../src/features/dataflow.js'; describe('extractDataflow — C#', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/dataflow-go.test.js b/tests/parsers/dataflow-go.test.ts similarity index 99% rename from tests/parsers/dataflow-go.test.js rename to tests/parsers/dataflow-go.test.ts index 2f73d294..bbc7f8ba 100644 --- a/tests/parsers/dataflow-go.test.js +++ b/tests/parsers/dataflow-go.test.ts @@ -6,7 +6,7 @@ import { createParsers } from '../../src/domain/parser.js'; import { extractDataflow } from '../../src/features/dataflow.js'; describe('extractDataflow — Go', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/dataflow-java.test.js b/tests/parsers/dataflow-java.test.ts similarity index 99% rename from tests/parsers/dataflow-java.test.js rename to tests/parsers/dataflow-java.test.ts index 44481a4e..21d5947c 100644 --- a/tests/parsers/dataflow-java.test.js +++ b/tests/parsers/dataflow-java.test.ts @@ -6,7 +6,7 @@ import { createParsers } from '../../src/domain/parser.js'; import { extractDataflow } from '../../src/features/dataflow.js'; describe('extractDataflow — Java', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/dataflow-javascript.test.js b/tests/parsers/dataflow-javascript.test.ts similarity index 99% rename from tests/parsers/dataflow-javascript.test.js rename to tests/parsers/dataflow-javascript.test.ts index bcbdf0c9..cb23eea8 100644 --- a/tests/parsers/dataflow-javascript.test.js +++ b/tests/parsers/dataflow-javascript.test.ts @@ -6,7 +6,7 @@ import { createParsers } from '../../src/domain/parser.js'; import { extractDataflow } from '../../src/features/dataflow.js'; describe('extractDataflow — JavaScript', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); @@ -329,7 +329,7 @@ describe('extractDataflow — JavaScript', () => { it('tracks x = foo() without const/let/var', () => { const data = parseAndExtract(` function update() { - let result; + let result: any; result = compute(); return result; } diff --git a/tests/parsers/dataflow-php.test.js b/tests/parsers/dataflow-php.test.ts similarity index 99% rename from tests/parsers/dataflow-php.test.js rename to tests/parsers/dataflow-php.test.ts index ef5ddcc9..b015e8e7 100644 --- a/tests/parsers/dataflow-php.test.js +++ b/tests/parsers/dataflow-php.test.ts @@ -6,7 +6,7 @@ import { createParsers } from '../../src/domain/parser.js'; import { extractDataflow } from '../../src/features/dataflow.js'; describe('extractDataflow — PHP', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/dataflow-python.test.js b/tests/parsers/dataflow-python.test.ts similarity index 99% rename from tests/parsers/dataflow-python.test.js rename to tests/parsers/dataflow-python.test.ts index e97aeea9..be37839b 100644 --- a/tests/parsers/dataflow-python.test.js +++ b/tests/parsers/dataflow-python.test.ts @@ -6,7 +6,7 @@ import { createParsers } from '../../src/domain/parser.js'; import { extractDataflow } from '../../src/features/dataflow.js'; describe('extractDataflow — Python', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/dataflow-ruby.test.js b/tests/parsers/dataflow-ruby.test.ts similarity index 99% rename from tests/parsers/dataflow-ruby.test.js rename to tests/parsers/dataflow-ruby.test.ts index 973c6bee..314f7739 100644 --- a/tests/parsers/dataflow-ruby.test.js +++ b/tests/parsers/dataflow-ruby.test.ts @@ -6,7 +6,7 @@ import { createParsers } from '../../src/domain/parser.js'; import { extractDataflow } from '../../src/features/dataflow.js'; describe('extractDataflow — Ruby', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/dataflow-rust.test.js b/tests/parsers/dataflow-rust.test.ts similarity index 99% rename from tests/parsers/dataflow-rust.test.js rename to tests/parsers/dataflow-rust.test.ts index f7200487..2520a79c 100644 --- a/tests/parsers/dataflow-rust.test.js +++ b/tests/parsers/dataflow-rust.test.ts @@ -6,7 +6,7 @@ import { createParsers } from '../../src/domain/parser.js'; import { extractDataflow } from '../../src/features/dataflow.js'; describe('extractDataflow — Rust', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/extended-kinds.test.js b/tests/parsers/extended-kinds.test.ts similarity index 99% rename from tests/parsers/extended-kinds.test.js rename to tests/parsers/extended-kinds.test.ts index ab1a8ccf..c10b1ea1 100644 --- a/tests/parsers/extended-kinds.test.js +++ b/tests/parsers/extended-kinds.test.ts @@ -20,7 +20,7 @@ import { // ── JavaScript ────────────────────────────────────────────────────────────── describe('JavaScript extended kinds', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); @@ -97,7 +97,7 @@ describe('JavaScript extended kinds', () => { // ── Python ────────────────────────────────────────────────────────────────── describe('Python extended kinds', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); @@ -155,7 +155,7 @@ describe('Python extended kinds', () => { // ── Go ────────────────────────────────────────────────────────────────────── describe('Go extended kinds', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); @@ -209,7 +209,7 @@ describe('Go extended kinds', () => { // ── Rust ───────────────────────────────────────────────────────────────────── describe('Rust extended kinds', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); @@ -276,7 +276,7 @@ describe('Rust extended kinds', () => { // ── Java ───────────────────────────────────────────────────────────────────── describe('Java extended kinds', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); @@ -335,7 +335,7 @@ describe('Java extended kinds', () => { // ── C# ────────────────────────────────────────────────────────────────────── describe('C# extended kinds', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); @@ -394,7 +394,7 @@ describe('C# extended kinds', () => { // ── Ruby ───────────────────────────────────────────────────────────────────── describe('Ruby extended kinds', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); @@ -447,7 +447,7 @@ describe('Ruby extended kinds', () => { // ── PHP ────────────────────────────────────────────────────────────────────── describe('PHP extended kinds', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/go.test.js b/tests/parsers/go.test.ts similarity index 99% rename from tests/parsers/go.test.js rename to tests/parsers/go.test.ts index ddb1efdf..73e02b6d 100644 --- a/tests/parsers/go.test.js +++ b/tests/parsers/go.test.ts @@ -2,7 +2,7 @@ import { beforeAll, describe, expect, it } from 'vitest'; import { createParsers, extractGoSymbols } from '../../src/domain/parser.js'; describe('Go parser', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/java.test.js b/tests/parsers/java.test.ts similarity index 99% rename from tests/parsers/java.test.js rename to tests/parsers/java.test.ts index 39bcf118..591bfa50 100644 --- a/tests/parsers/java.test.js +++ b/tests/parsers/java.test.ts @@ -2,7 +2,7 @@ import { beforeAll, describe, expect, it } from 'vitest'; import { createParsers, extractJavaSymbols } from '../../src/domain/parser.js'; describe('Java parser', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/javascript.test.js b/tests/parsers/javascript.test.ts similarity index 99% rename from tests/parsers/javascript.test.js rename to tests/parsers/javascript.test.ts index 294ba39e..f68cffd8 100644 --- a/tests/parsers/javascript.test.js +++ b/tests/parsers/javascript.test.ts @@ -9,7 +9,7 @@ import { beforeAll, describe, expect, it } from 'vitest'; import { createParsers, extractSymbols } from '../../src/domain/parser.js'; describe('JavaScript parser', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/php.test.js b/tests/parsers/php.test.ts similarity index 99% rename from tests/parsers/php.test.js rename to tests/parsers/php.test.ts index 106ba306..74a5436e 100644 --- a/tests/parsers/php.test.js +++ b/tests/parsers/php.test.ts @@ -2,7 +2,7 @@ import { beforeAll, describe, expect, it } from 'vitest'; import { createParsers, extractPHPSymbols } from '../../src/domain/parser.js'; describe('PHP parser', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/ruby.test.js b/tests/parsers/ruby.test.ts similarity index 99% rename from tests/parsers/ruby.test.js rename to tests/parsers/ruby.test.ts index eff4a403..03f1d67c 100644 --- a/tests/parsers/ruby.test.js +++ b/tests/parsers/ruby.test.ts @@ -2,7 +2,7 @@ import { beforeAll, describe, expect, it } from 'vitest'; import { createParsers, extractRubySymbols } from '../../src/domain/parser.js'; describe('Ruby parser', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/rust.test.js b/tests/parsers/rust.test.ts similarity index 99% rename from tests/parsers/rust.test.js rename to tests/parsers/rust.test.ts index e6ee7dae..0828fee4 100644 --- a/tests/parsers/rust.test.js +++ b/tests/parsers/rust.test.ts @@ -2,7 +2,7 @@ import { beforeAll, describe, expect, it } from 'vitest'; import { createParsers, extractRustSymbols } from '../../src/domain/parser.js'; describe('Rust parser', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/unified.test.js b/tests/parsers/unified.test.ts similarity index 100% rename from tests/parsers/unified.test.js rename to tests/parsers/unified.test.ts diff --git a/tests/presentation/colors.test.js b/tests/presentation/colors.test.ts similarity index 100% rename from tests/presentation/colors.test.js rename to tests/presentation/colors.test.ts diff --git a/tests/presentation/queries-cli.test.js b/tests/presentation/queries-cli.test.ts similarity index 99% rename from tests/presentation/queries-cli.test.js rename to tests/presentation/queries-cli.test.ts index d5a41ae2..18fd76ea 100644 --- a/tests/presentation/queries-cli.test.js +++ b/tests/presentation/queries-cli.test.ts @@ -42,7 +42,7 @@ const { fileExports } = await import('../../src/presentation/queries-cli/exports const { moduleMap, roles } = await import('../../src/presentation/queries-cli/overview.js'); // ── Helpers ──────────────────────────────────────────────────────── -let lines; +let lines: any; beforeEach(() => { lines = []; diff --git a/tests/presentation/result-formatter.test.js b/tests/presentation/result-formatter.test.ts similarity index 99% rename from tests/presentation/result-formatter.test.js rename to tests/presentation/result-formatter.test.ts index b524a297..88e4f432 100644 --- a/tests/presentation/result-formatter.test.js +++ b/tests/presentation/result-formatter.test.ts @@ -17,7 +17,7 @@ vi.mock('../../src/shared/paginate.js', () => ({ const { outputResult } = await import('../../src/presentation/result-formatter.js'); describe('outputResult', () => { - let logSpy; + let logSpy: any; beforeEach(() => { logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); diff --git a/tests/presentation/table.test.js b/tests/presentation/table.test.ts similarity index 100% rename from tests/presentation/table.test.js rename to tests/presentation/table.test.ts diff --git a/tests/resolution/parity.test.js b/tests/resolution/parity.test.ts similarity index 100% rename from tests/resolution/parity.test.js rename to tests/resolution/parity.test.ts diff --git a/tests/search/embedder-search.test.js b/tests/search/embedder-search.test.ts similarity index 99% rename from tests/search/embedder-search.test.js rename to tests/search/embedder-search.test.ts index 6216c986..38582fd3 100644 --- a/tests/search/embedder-search.test.js +++ b/tests/search/embedder-search.test.ts @@ -97,8 +97,8 @@ function captureLog(fn) { // C: "function authMiddleware (auth Middleware) in src/middleware.js" // D: "function formatDate (format Date) in src/utils.js" -let tmpDir, dbPath; -let noFtsDir, noFtsDbPath; +let tmpDir: string, dbPath: string; +let noFtsDir: string, noFtsDbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-test-')); diff --git a/tests/search/embedding-regression.test.js b/tests/search/embedding-regression.test.ts similarity index 99% rename from tests/search/embedding-regression.test.js rename to tests/search/embedding-regression.test.ts index 8e618185..a0391b7e 100644 --- a/tests/search/embedding-regression.test.js +++ b/tests/search/embedding-regression.test.ts @@ -51,7 +51,7 @@ export function main() { `.trimStart(), }; -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; describe.skipIf(!hasTransformers)('embedding regression (real model)', () => { beforeAll(async () => { diff --git a/tests/search/embedding-strategy.test.js b/tests/search/embedding-strategy.test.ts similarity index 99% rename from tests/search/embedding-strategy.test.js rename to tests/search/embedding-strategy.test.ts index a8a50a2c..a8e58828 100644 --- a/tests/search/embedding-strategy.test.js +++ b/tests/search/embedding-strategy.test.ts @@ -70,7 +70,7 @@ const FIXTURE_FILES = { ].join('\n'), }; -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-strategy-test-')); @@ -290,7 +290,7 @@ describe('FTS5 index built alongside embeddings', () => { }); describe('context window overflow detection', () => { - let bigDir, bigDbPath; + let bigDir: string, bigDbPath: string; beforeAll(() => { // Create a file with a very large function that will overflow minilm's 256-token window diff --git a/tests/unit/boundaries.test.js b/tests/unit/boundaries.test.ts similarity index 99% rename from tests/unit/boundaries.test.js rename to tests/unit/boundaries.test.ts index 0f2642e7..d3f9b440 100644 --- a/tests/unit/boundaries.test.js +++ b/tests/unit/boundaries.test.ts @@ -202,7 +202,7 @@ describe('PRESETS', () => { // ─── evaluateBoundaries ────────────────────────────────────────────── describe('evaluateBoundaries', () => { - let tmpDir, dbPath, db; + let tmpDir: string, dbPath: string, db: any; function insertNode(database, name, kind, file, line) { return database diff --git a/tests/unit/builder.test.js b/tests/unit/builder.test.ts similarity index 99% rename from tests/unit/builder.test.js rename to tests/unit/builder.test.ts index 7dc7d958..2d15c664 100644 --- a/tests/unit/builder.test.js +++ b/tests/unit/builder.test.ts @@ -11,7 +11,7 @@ import path from 'node:path'; import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; import { collectFiles, loadPathAliases, readFileSafe } from '../../src/domain/graph/builder.js'; -let tmpDir; +let tmpDir: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-builder-')); diff --git a/tests/unit/cfg.test.js b/tests/unit/cfg.test.ts similarity index 99% rename from tests/unit/cfg.test.js rename to tests/unit/cfg.test.ts index 50dc06dd..743ee7bd 100644 --- a/tests/unit/cfg.test.js +++ b/tests/unit/cfg.test.ts @@ -13,7 +13,7 @@ import { createParsers } from '../../src/domain/parser.js'; import { buildFunctionCFG, makeCfgRules } from '../../src/features/cfg.js'; import { COMPLEXITY_RULES } from '../../src/features/complexity.js'; -let jsParser; +let jsParser: any; beforeAll(async () => { const parsers = await createParsers(); @@ -493,7 +493,7 @@ function makeLangHelpers(langId, parsers) { // ── Python ─────────────────────────────────────────────────────────────── describe('buildFunctionCFG — Python', () => { - let helpers; + let helpers: any; beforeAll(async () => { const parsers = await createParsers(); @@ -589,7 +589,7 @@ def guard(x): // ── Go ─────────────────────────────────────────────────────────────────── describe('buildFunctionCFG — Go', () => { - let helpers; + let helpers: any; beforeAll(async () => { const parsers = await createParsers(); @@ -684,7 +684,7 @@ func sw(x int) { // ── Rust ───────────────────────────────────────────────────────────────── describe('buildFunctionCFG — Rust', () => { - let helpers; + let helpers: any; beforeAll(async () => { const parsers = await createParsers(); @@ -790,7 +790,7 @@ fn match_fn(x: i32) { // ── Java ───────────────────────────────────────────────────────────────── describe('buildFunctionCFG — Java', () => { - let helpers; + let helpers: any; beforeAll(async () => { const parsers = await createParsers(); @@ -913,7 +913,7 @@ class A { // ── C# ─────────────────────────────────────────────────────────────────── describe('buildFunctionCFG — C#', () => { - let helpers; + let helpers: any; beforeAll(async () => { const parsers = await createParsers(); @@ -1000,7 +1000,7 @@ class A { // ── Ruby ───────────────────────────────────────────────────────────────── describe('buildFunctionCFG — Ruby', () => { - let helpers; + let helpers: any; beforeAll(async () => { const parsers = await createParsers(); @@ -1102,7 +1102,7 @@ end // ── PHP ────────────────────────────────────────────────────────────────── describe('buildFunctionCFG — PHP', () => { - let helpers; + let helpers: any; beforeAll(async () => { const parsers = await createParsers(); diff --git a/tests/unit/change-journal.test.js b/tests/unit/change-journal.test.ts similarity index 99% rename from tests/unit/change-journal.test.js rename to tests/unit/change-journal.test.ts index 3595c674..1812e3f2 100644 --- a/tests/unit/change-journal.test.js +++ b/tests/unit/change-journal.test.ts @@ -16,7 +16,7 @@ import { rotateIfNeeded, } from '../../src/domain/graph/change-journal.js'; -let tmpDir; +let tmpDir: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-change-journal-')); diff --git a/tests/unit/complexity.test.js b/tests/unit/complexity.test.ts similarity index 99% rename from tests/unit/complexity.test.js rename to tests/unit/complexity.test.ts index 776df84c..83bd87fe 100644 --- a/tests/unit/complexity.test.js +++ b/tests/unit/complexity.test.ts @@ -16,7 +16,7 @@ import { HALSTEAD_RULES, } from '../../src/features/complexity.js'; -let jsParser; +let jsParser: any; beforeAll(async () => { const parsers = await createParsers(); @@ -458,7 +458,7 @@ describe('computeMaintainabilityIndex', () => { function makeHelpers(langId, parsersPromise) { const rules = COMPLEXITY_RULES.get(langId); - let parser; + let parser: any; let available = false; beforeAll(async () => { const parsers = await parsersPromise; @@ -499,7 +499,7 @@ function makeHelpers(langId, parsersPromise) { } // Shared parsers promise to avoid re-initializing per suite -let _parsersPromise; +let _parsersPromise: any; function sharedParsers() { if (!_parsersPromise) _parsersPromise = createParsers(); return _parsersPromise; diff --git a/tests/unit/config.test.js b/tests/unit/config.test.ts similarity index 99% rename from tests/unit/config.test.js rename to tests/unit/config.test.ts index 401046da..bba8c190 100644 --- a/tests/unit/config.test.js +++ b/tests/unit/config.test.ts @@ -24,7 +24,7 @@ vi.mock('node:child_process', async (importOriginal) => { }; }); -let tmpDir; +let tmpDir: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-config-')); @@ -374,7 +374,7 @@ describe('applyEnvOverrides', () => { }); describe('resolveSecrets', () => { - let mockExecFile; + let mockExecFile: any; beforeAll(async () => { const cp = await import('node:child_process'); @@ -486,7 +486,7 @@ describe('resolveSecrets', () => { describe('apiKeyCommand integration', () => { const ENV_KEYS = ['CODEGRAPH_LLM_PROVIDER', 'CODEGRAPH_LLM_API_KEY', 'CODEGRAPH_LLM_MODEL']; - let mockExecFile; + let mockExecFile: any; beforeAll(async () => { const cp = await import('node:child_process'); diff --git a/tests/unit/constants.test.js b/tests/unit/constants.test.ts similarity index 100% rename from tests/unit/constants.test.js rename to tests/unit/constants.test.ts diff --git a/tests/unit/db.test.js b/tests/unit/db.test.ts similarity index 99% rename from tests/unit/db.test.js rename to tests/unit/db.test.ts index fd0a595c..055afac9 100644 --- a/tests/unit/db.test.js +++ b/tests/unit/db.test.ts @@ -32,7 +32,7 @@ import { setBuildMeta, } from '../../src/db/index.js'; -let tmpDir; +let tmpDir: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-db-')); @@ -261,9 +261,9 @@ describe('findRepoRoot', () => { }); describe('findDbPath with git ceiling', () => { - let outerDir; - let worktreeRoot; - let innerDir; + let outerDir: string; + let worktreeRoot: string; + let innerDir: string; beforeAll(() => { // Simulate a worktree-inside-repo layout: diff --git a/tests/unit/errors.test.js b/tests/unit/errors.test.ts similarity index 100% rename from tests/unit/errors.test.js rename to tests/unit/errors.test.ts diff --git a/tests/unit/in-memory-repository.test.js b/tests/unit/in-memory-repository.test.ts similarity index 100% rename from tests/unit/in-memory-repository.test.js rename to tests/unit/in-memory-repository.test.ts diff --git a/tests/unit/index-exports.test.js b/tests/unit/index-exports.test.ts similarity index 84% rename from tests/unit/index-exports.test.js rename to tests/unit/index-exports.test.ts index caa5a5fa..ad636436 100644 --- a/tests/unit/index-exports.test.js +++ b/tests/unit/index-exports.test.ts @@ -1,12 +1,12 @@ import { readFileSync } from 'node:fs'; -import { createRequire } from 'node:module'; +import { createRequire, register } from 'node:module'; import { dirname, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import { describe, expect, it } from 'vitest'; -// CJS require goes through Node's native loader — needs Node >= 22.6 for .ts -const [_major, _minor] = process.versions.node.split('.').map(Number); -const canStripTypes = _major > 22 || (_major === 22 && _minor >= 6); +// Register .js → .ts resolve hook so CJS wrapper's import('./index.ts') +// can resolve the .js import specifiers used throughout the source. +register(new URL('../../scripts/ts-resolve-hooks.ts', import.meta.url)); const __dirname = dirname(fileURLToPath(import.meta.url)); const pkg = JSON.parse(readFileSync(resolve(__dirname, '../../package.json'), 'utf8')); @@ -26,7 +26,7 @@ describe('index.js re-exports', () => { expect(typeof mod).toBe('object'); }); - it.skipIf(!canStripTypes)('CJS wrapper resolves to the same exports', async () => { + it('CJS wrapper resolves to the same exports', async () => { const require = createRequire(import.meta.url); const cjs = await require('../../src/index.cjs'); const esm = await import('../../src/index.js'); diff --git a/tests/unit/journal.test.js b/tests/unit/journal.test.ts similarity index 99% rename from tests/unit/journal.test.js rename to tests/unit/journal.test.ts index 27769f2a..8428e752 100644 --- a/tests/unit/journal.test.js +++ b/tests/unit/journal.test.ts @@ -13,7 +13,7 @@ import { writeJournalHeader, } from '../../src/domain/graph/journal.js'; -let tmpDir; +let tmpDir: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-journal-')); diff --git a/tests/unit/logger.test.js b/tests/unit/logger.test.ts similarity index 99% rename from tests/unit/logger.test.js rename to tests/unit/logger.test.ts index fb54863d..22fbc1ab 100644 --- a/tests/unit/logger.test.js +++ b/tests/unit/logger.test.ts @@ -13,7 +13,7 @@ import { } from '../../src/infrastructure/logger.js'; describe('logger', () => { - let stderrSpy; + let stderrSpy: any; afterEach(() => { setVerbose(false); diff --git a/tests/unit/mcp.test.js b/tests/unit/mcp.test.ts similarity index 100% rename from tests/unit/mcp.test.js rename to tests/unit/mcp.test.ts diff --git a/tests/unit/normalize-symbol.test.js b/tests/unit/normalize-symbol.test.ts similarity index 100% rename from tests/unit/normalize-symbol.test.js rename to tests/unit/normalize-symbol.test.ts diff --git a/tests/unit/owners.test.js b/tests/unit/owners.test.ts similarity index 99% rename from tests/unit/owners.test.js rename to tests/unit/owners.test.ts index 52ed123c..f49a3939 100644 --- a/tests/unit/owners.test.js +++ b/tests/unit/owners.test.ts @@ -160,7 +160,7 @@ describe('matchOwners', () => { // ─── parseCodeowners ───────────────────────────────────────────────── describe('parseCodeowners', () => { - let tmpDir; + let tmpDir: string; beforeEach(() => { clearCodeownersCache(); diff --git a/tests/unit/parser.test.js b/tests/unit/parser.test.ts similarity index 100% rename from tests/unit/parser.test.js rename to tests/unit/parser.test.ts diff --git a/tests/unit/prompt-install.test.js b/tests/unit/prompt-install.test.ts similarity index 98% rename from tests/unit/prompt-install.test.js rename to tests/unit/prompt-install.test.ts index d7e09981..280620b1 100644 --- a/tests/unit/prompt-install.test.js +++ b/tests/unit/prompt-install.test.ts @@ -11,10 +11,10 @@ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; describe('loadTransformers install prompt', () => { - let exitSpy; - let errorSpy; - let logSpy; - let origTTY; + let exitSpy: any; + let errorSpy: any; + let logSpy: any; + let origTTY: any; beforeEach(() => { vi.resetModules(); diff --git a/tests/unit/purge-files.test.js b/tests/unit/purge-files.test.ts similarity index 100% rename from tests/unit/purge-files.test.js rename to tests/unit/purge-files.test.ts diff --git a/tests/unit/queries-unit.test.js b/tests/unit/queries-unit.test.ts similarity index 99% rename from tests/unit/queries-unit.test.js rename to tests/unit/queries-unit.test.ts index 19a6b2aa..33ceafc4 100644 --- a/tests/unit/queries-unit.test.js +++ b/tests/unit/queries-unit.test.ts @@ -60,7 +60,7 @@ function insertEdge(db, sourceId, targetId, kind) { // testHelper -> handleRequest // ChildService.process -> BaseService.process (intra-hierarchy) -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-queries-unit-')); diff --git a/tests/unit/query-builder.test.js b/tests/unit/query-builder.test.ts similarity index 99% rename from tests/unit/query-builder.test.js rename to tests/unit/query-builder.test.ts index a2a15e51..fa755a46 100644 --- a/tests/unit/query-builder.test.js +++ b/tests/unit/query-builder.test.ts @@ -158,7 +158,7 @@ describe('collectFile', () => { // ─── NodeQuery ─────────────────────────────────────────────────────── describe('NodeQuery', () => { - let db; + let db: any; beforeEach(() => { db = new Database(':memory:'); diff --git a/tests/unit/registry.test.js b/tests/unit/registry.test.ts similarity index 98% rename from tests/unit/registry.test.js rename to tests/unit/registry.test.ts index c4456c1d..9a927804 100644 --- a/tests/unit/registry.test.js +++ b/tests/unit/registry.test.ts @@ -15,12 +15,8 @@ import { unregisterRepo, } from '../../src/infrastructure/registry.js'; -// Child processes load .ts files natively — requires Node >= 22.6 type stripping -const [_major, _minor] = process.versions.node.split('.').map(Number); -const canStripTypes = _major > 22 || (_major === 22 && _minor >= 6); - -let tmpDir; -let registryPath; +let tmpDir: string; +let registryPath: string; beforeEach(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-registry-')); @@ -38,11 +34,15 @@ describe('REGISTRY_PATH', () => { expect(REGISTRY_PATH).toBe(path.join(os.homedir(), '.codegraph', 'registry.json')); }); - it.skipIf(!canStripTypes)('respects CODEGRAPH_REGISTRY_PATH env var', () => { + it('respects CODEGRAPH_REGISTRY_PATH env var', () => { const customPath = path.join(tmpDir, 'custom', 'registry.json'); + const loaderUrl = new URL('../../scripts/ts-resolve-loader.ts', import.meta.url).href; const result = execFileSync( 'node', [ + '--experimental-strip-types', + '--import', + loaderUrl, '--input-type=module', '-e', `import { REGISTRY_PATH } from './src/infrastructure/registry.js'; process.stdout.write(REGISTRY_PATH);`, diff --git a/tests/unit/repository-parity.test.js b/tests/unit/repository-parity.test.ts similarity index 99% rename from tests/unit/repository-parity.test.js rename to tests/unit/repository-parity.test.ts index 86d11610..a3bac79d 100644 --- a/tests/unit/repository-parity.test.js +++ b/tests/unit/repository-parity.test.ts @@ -149,9 +149,9 @@ describe.each([ { label: 'SqliteRepository', seed: seedSqliteRepo }, { label: 'InMemoryRepository', seed: seedInMemoryRepo }, ])('Repository parity: $label', ({ seed }) => { - let repo; - let ids; - let dbToClose; + let repo: any; + let ids: any; + let dbToClose: any; beforeEach(() => { const result = seed(); diff --git a/tests/unit/repository.test.js b/tests/unit/repository.test.ts similarity index 99% rename from tests/unit/repository.test.js rename to tests/unit/repository.test.ts index 37d3defa..5966c2d8 100644 --- a/tests/unit/repository.test.js +++ b/tests/unit/repository.test.ts @@ -13,7 +13,7 @@ import { } from '../../src/db/repository/nodes.js'; describe('repository', () => { - let db; + let db: any; beforeEach(() => { db = new Database(':memory:'); diff --git a/tests/unit/resolve.test.js b/tests/unit/resolve.test.ts similarity index 99% rename from tests/unit/resolve.test.js rename to tests/unit/resolve.test.ts index 8f6227f8..eaa8b5aa 100644 --- a/tests/unit/resolve.test.js +++ b/tests/unit/resolve.test.ts @@ -25,7 +25,7 @@ import { // ─── Temp project setup ────────────────────────────────────────────── -let tmpDir; +let tmpDir: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-resolve-')); @@ -257,7 +257,7 @@ describe('parseBareSpecifier', () => { // ─── resolveViaExports ─────────────────────────────────────────────── describe('resolveViaExports', () => { - let pkgRoot; + let pkgRoot: string; beforeAll(() => { clearExportsCache(); @@ -392,7 +392,7 @@ describe('resolveViaExports', () => { // ─── resolveImportPathJS with exports ──────────────────────────────── describe('resolveImportPathJS with package.json exports', () => { - let pkgRoot; + let pkgRoot: string; beforeAll(() => { clearExportsCache(); @@ -428,7 +428,7 @@ describe('resolveImportPathJS with package.json exports', () => { // ─── resolveViaWorkspace ───────────────────────────────────────────── describe('resolveViaWorkspace', () => { - let wsRoot; + let wsRoot: string; beforeAll(() => { wsRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-ws-')); @@ -521,7 +521,7 @@ describe('resolveViaWorkspace', () => { // ─── resolveImportPathJS with workspaces ───────────────────────────── describe('resolveImportPathJS with workspace resolution', () => { - let wsRoot; + let wsRoot: string; beforeAll(() => { wsRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-ws-resolve-')); @@ -588,7 +588,7 @@ describe('resolveImportPathJS with workspace resolution', () => { // ─── computeConfidenceJS with workspace boost ──────────────────────── describe('computeConfidenceJS workspace confidence', () => { - let wsRoot; + let wsRoot: string; beforeAll(() => { wsRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-ws-conf-')); diff --git a/tests/unit/roles.test.js b/tests/unit/roles.test.ts similarity index 99% rename from tests/unit/roles.test.js rename to tests/unit/roles.test.ts index cf4fd5e6..d729f8e0 100644 --- a/tests/unit/roles.test.js +++ b/tests/unit/roles.test.ts @@ -18,7 +18,7 @@ import { beforeEach, describe, expect, it } from 'vitest'; import { initSchema } from '../../src/db/index.js'; import { classifyNodeRoles } from '../../src/features/structure.js'; -let db; +let db: any; function setup() { db = new Database(':memory:'); diff --git a/tests/unit/snapshot.test.js b/tests/unit/snapshot.test.ts similarity index 99% rename from tests/unit/snapshot.test.js rename to tests/unit/snapshot.test.ts index b12b912a..ec149252 100644 --- a/tests/unit/snapshot.test.js +++ b/tests/unit/snapshot.test.ts @@ -12,8 +12,8 @@ import { validateSnapshotName, } from '../../src/features/snapshot.js'; -let tmpDir; -let dbPath; +let tmpDir: string; +let dbPath: string; function createTestDb(filePath) { fs.mkdirSync(path.dirname(filePath), { recursive: true }); diff --git a/tests/unit/structure.test.js b/tests/unit/structure.test.ts similarity index 99% rename from tests/unit/structure.test.js rename to tests/unit/structure.test.ts index fd57e487..77aa3088 100644 --- a/tests/unit/structure.test.js +++ b/tests/unit/structure.test.ts @@ -10,7 +10,7 @@ import { beforeEach, describe, expect, it } from 'vitest'; import { initSchema } from '../../src/db/index.js'; import { buildStructure } from '../../src/features/structure.js'; -let db; +let db: any; function setup() { db = new Database(':memory:'); diff --git a/tests/unit/update-check.test.js b/tests/unit/update-check.test.ts similarity index 99% rename from tests/unit/update-check.test.js rename to tests/unit/update-check.test.ts index 78bc204f..46621d8b 100644 --- a/tests/unit/update-check.test.js +++ b/tests/unit/update-check.test.ts @@ -8,8 +8,8 @@ import { semverCompare, } from '../../src/infrastructure/update-check.js'; -let tmpDir; -let cachePath; +let tmpDir: string; +let cachePath: string; beforeEach(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-update-')); diff --git a/tests/unit/visitor.test.js b/tests/unit/visitor.test.ts similarity index 99% rename from tests/unit/visitor.test.js rename to tests/unit/visitor.test.ts index e15571ec..62dc5b3e 100644 --- a/tests/unit/visitor.test.js +++ b/tests/unit/visitor.test.ts @@ -4,7 +4,7 @@ import { describe, expect, it } from 'vitest'; // We need a tree-sitter tree to test. Use the JS parser. -let parse; +let parse: any; async function ensureParser() { if (parse) return; diff --git a/tsconfig.json b/tsconfig.json index 613956a9..99220394 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -44,10 +44,6 @@ "noPropertyAccessFromIndexSignature": true, "exactOptionalPropertyTypes": false, - /* Migration support */ - "allowJs": true, - "checkJs": false, - /* Misc */ "skipLibCheck": true, "forceConsistentCasingInFileNames": true, diff --git a/vitest.config.js b/vitest.config.js deleted file mode 100644 index c3d7fc18..00000000 --- a/vitest.config.js +++ /dev/null @@ -1,61 +0,0 @@ -import { existsSync } from 'node:fs'; -import { dirname, resolve } from 'node:path'; -import { fileURLToPath, pathToFileURL } from 'node:url'; -import { defineConfig } from 'vitest/config'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); -const loaderPath = pathToFileURL(resolve(__dirname, 'scripts/ts-resolve-loader.js')).href; -const [major, minor] = process.versions.node.split('.').map(Number); -const supportsStripTypes = major > 22 || (major === 22 && minor >= 6); -const supportsHooks = major > 20 || (major === 20 && minor >= 6); -const existing = process.env.NODE_OPTIONS || ''; - -/** - * During the JS → TS migration, some .js files import from modules that have - * already been renamed to .ts. Vite only auto-resolves .js→.ts when the - * *importer* is itself a .ts file. This plugin fills the gap: when a .js - * import target doesn't exist on disk, it tries the .ts counterpart. - */ -function jsToTsResolver() { - return { - name: 'js-to-ts-resolver', - enforce: 'pre', - resolveId(source, importer) { - if (!importer || !source.endsWith('.js')) return null; - // Only handle relative/absolute paths, not bare specifiers - if (!source.startsWith('.') && !source.startsWith('/')) return null; - const importerPath = importer.startsWith('file://') - ? fileURLToPath(importer) - : importer; - const fsPath = resolve(dirname(importerPath), source); - if (!existsSync(fsPath)) { - const tsPath = fsPath.replace(/\.js$/, '.ts'); - if (existsSync(tsPath)) return tsPath; - } - return null; - }, - }; -} - -export default defineConfig({ - plugins: [jsToTsResolver()], - test: { - globals: true, - testTimeout: 30000, - hookTimeout: 30000, - exclude: ['**/node_modules/**', '**/.git/**', '**/.claude/**'], - // Register the .js→.ts resolve loader for Node's native ESM resolver. - // This covers require() calls and child processes spawned by tests. - env: { - NODE_OPTIONS: [ - existing, - supportsStripTypes && - !existing.includes('--experimental-strip-types') && - !existing.includes('--strip-types') - ? (major >= 23 ? '--strip-types' : '--experimental-strip-types') - : '', - existing.includes(loaderPath) ? '' : (supportsHooks ? `--import ${loaderPath}` : ''), - ].filter(Boolean).join(' '), - }, - }, -}); diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 00000000..76d7f5e0 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,25 @@ +import { defineConfig } from 'vitest/config'; + +const [major, minor] = process.versions.node.split('.').map(Number); +const existing = process.env.NODE_OPTIONS || ''; +const supportsStripTypes = major > 22 || (major === 22 && minor >= 6); +const stripFlag = major >= 23 ? '--strip-types' : '--experimental-strip-types'; + +export default defineConfig({ + test: { + globals: true, + testTimeout: 30000, + hookTimeout: 30000, + exclude: ['**/node_modules/**', '**/.git/**', '**/.claude/**'], + // Ensure child processes spawned by tests (e.g. CLI integration tests) + // can load .ts files via Node's built-in type stripping. + env: { + NODE_OPTIONS: [ + existing, + supportsStripTypes && !existing.includes('--experimental-strip-types') && !existing.includes('--strip-types') + ? stripFlag + : '', + ].filter(Boolean).join(' '), + }, + }, +});