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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,7 @@ The `.codegraph/config.json` file controls indexing:
| Vue | `.vue` | Full support (script + script-setup extraction, Nuxt page/API/middleware routes) |
| Liquid | `.liquid` | Full support |
| Pascal / Delphi | `.pas`, `.dpr`, `.dpk`, `.lpr` | Full support (classes, records, interfaces, enums, DFM/FMX form files) |
| Nix | `.nix` | Full support |

## Troubleshooting

Expand Down
27 changes: 27 additions & 0 deletions __tests__/extraction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ describe('Language Detection', () => {
expect(detectLanguage('main.dart')).toBe('dart');
});

it('should detect Nix files', () => {
expect(detectLanguage('default.nix')).toBe('nix');
});

it('should return unknown for unsupported extensions', () => {
expect(detectLanguage('styles.css')).toBe('unknown');
expect(detectLanguage('data.json')).toBe('unknown');
Expand All @@ -105,6 +109,7 @@ describe('Language Support', () => {
expect(isLanguageSupported('typescript')).toBe(true);
expect(isLanguageSupported('python')).toBe(true);
expect(isLanguageSupported('go')).toBe(true);
expect(isLanguageSupported('nix')).toBe(true);
expect(isLanguageSupported('unknown')).toBe(false);
});

Expand All @@ -122,6 +127,28 @@ describe('Language Support', () => {
expect(languages).toContain('swift');
expect(languages).toContain('kotlin');
expect(languages).toContain('dart');
expect(languages).toContain('nix');
});
});

describe('Nix Extraction', () => {
it('should extract Nix bindings and imports', () => {
const code = `{
hello = "world";
package = import ./default.nix;
}`;

const result = extractFromSource('default.nix', code);
const hello = result.nodes.find((n) => n.kind === 'variable' && n.name === 'hello');
const packageNode = result.nodes.find((n) => n.kind === 'variable' && n.name === 'package');
const importNode = result.nodes.find((n) => n.kind === 'import');

expect(hello).toBeDefined();
expect(packageNode).toBeDefined();
expect(importNode).toMatchObject({
kind: 'import',
name: './default.nix',
});
});
});

Expand Down
7 changes: 5 additions & 2 deletions src/extraction/grammars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const WASM_GRAMMAR_FILES: Record<GrammarLanguage, string> = {
swift: 'tree-sitter-swift.wasm',
kotlin: 'tree-sitter-kotlin.wasm',
dart: 'tree-sitter-dart.wasm',
nix: 'tree-sitter-nix.wasm',
pascal: 'tree-sitter-pascal.wasm',
scala: 'tree-sitter-scala.wasm',
};
Expand Down Expand Up @@ -67,6 +68,7 @@ export const EXTENSION_MAP: Record<string, Language> = {
'.kt': 'kotlin',
'.kts': 'kotlin',
'.dart': 'dart',
'.nix': 'nix',
'.liquid': 'liquid',
'.svelte': 'svelte',
'.vue': 'vue',
Expand Down Expand Up @@ -125,8 +127,8 @@ export async function loadGrammarsForLanguages(languages: Language[]): Promise<v
for (const lang of toLoad) {
const wasmFile = WASM_GRAMMAR_FILES[lang];
try {
// Pascal and Scala ship their own WASMs (not in tree-sitter-wasms)
const wasmPath = (lang === 'pascal' || lang === 'scala')
// Pascal, Scala, and Nix ship their own WASMs (not in tree-sitter-wasms)
const wasmPath = (lang === 'pascal' || lang === 'scala' || lang === 'nix')
? path.join(__dirname, 'wasm', wasmFile)
: require.resolve(`tree-sitter-wasms/out/${wasmFile}`);
const language = await WasmLanguage.load(wasmPath);
Expand Down Expand Up @@ -286,6 +288,7 @@ export function getLanguageDisplayName(language: Language): string {
swift: 'Swift',
kotlin: 'Kotlin',
dart: 'Dart',
nix: 'Nix',
svelte: 'Svelte',
vue: 'Vue',
liquid: 'Liquid',
Expand Down
2 changes: 2 additions & 0 deletions src/extraction/languages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { kotlinExtractor } from './kotlin';
import { dartExtractor } from './dart';
import { pascalExtractor } from './pascal';
import { scalaExtractor } from './scala';
import { nixExtractor } from './nix';

export const EXTRACTORS: Partial<Record<Language, LanguageExtractor>> = {
typescript: typescriptExtractor,
Expand All @@ -41,6 +42,7 @@ export const EXTRACTORS: Partial<Record<Language, LanguageExtractor>> = {
swift: swiftExtractor,
kotlin: kotlinExtractor,
dart: dartExtractor,
nix: nixExtractor,
pascal: pascalExtractor,
scala: scalaExtractor,
};
51 changes: 51 additions & 0 deletions src/extraction/languages/nix.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { getNodeText, getChildByField } from '../tree-sitter-helpers';
import type { Node as SyntaxNode } from 'web-tree-sitter';
import type { LanguageExtractor, ImportInfo } from '../tree-sitter-types';

export const nixExtractor: LanguageExtractor = {
functionTypes: [],
classTypes: [],
methodTypes: [],
interfaceTypes: [],
structTypes: [],
enumTypes: [],
typeAliasTypes: [],
importTypes: ['apply_expression'],
callTypes: [],
variableTypes: ['binding'],
nameField: 'name',
bodyField: 'body',
paramsField: 'arguments',

visitNode: (node: SyntaxNode, ctx) => {
if (node.type !== 'binding') return false;

const attrpath = getChildByField(node, 'attrpath');
const name = attrpath ? getNodeText(attrpath, ctx.source) : getNodeText(node, ctx.source);
const valueNode = getChildByField(node, 'expression');
const signature = valueNode ? getNodeText(valueNode, ctx.source) : undefined;

ctx.createNode('variable', name, node, { signature });

if (valueNode) {
ctx.visitNode(valueNode);
}

return true;
},

extractImport: (node: SyntaxNode, source: string) => {
if (node.type !== 'apply_expression') return null;
const functionNode = getChildByField(node, 'function');
if (!functionNode || getNodeText(functionNode, source) !== 'import') return null;

const argument = getChildByField(node, 'argument');
if (!argument) return null;

const moduleName = getNodeText(argument, source).replace(/^['"]|['"]$/g, '');
return {
moduleName,
signature: getNodeText(node, source).trim(),
} as ImportInfo;
},
};
Binary file added src/extraction/wasm/tree-sitter-nix.wasm
Binary file not shown.
4 changes: 4 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,13 @@ export const LANGUAGES = [
'swift',
'kotlin',
'dart',
'nix',
'svelte',
'vue',
'liquid',
'pascal',
'scala',
'nix',
'unknown',
] as const;

Expand Down Expand Up @@ -529,6 +531,8 @@ export const DEFAULT_CONFIG: CodeGraphConfig = {
'**/*.kts',
// Dart
'**/*.dart',
// Nix
'**/*.nix',
// Svelte
'**/*.svelte',
// Vue
Expand Down