giga-code
Version:
A personal AI CLI assistant powered by Grok for local development.
541 lines • 22.4 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CodeParser = void 0;
const path = __importStar(require("path"));
class CodeParser {
static parseFile(filePath, content) {
const ext = path.extname(filePath).toLowerCase();
const language = this.LANGUAGE_PARSERS[ext];
if (!language) {
// Unknown file type, treat as plain text
return this.parseAsPlainText(filePath, content);
}
const lines = content.split('\n');
switch (language) {
case 'typescript':
case 'javascript':
return this.parseTypeScript(filePath, content, lines);
case 'python':
return this.parsePython(filePath, content, lines);
case 'java':
return this.parseJava(filePath, content, lines);
case 'cpp':
case 'c':
return this.parseC(filePath, content, lines);
case 'go':
return this.parseGo(filePath, content, lines);
case 'rust':
return this.parseRust(filePath, content, lines);
case 'json':
return this.parseJSON(filePath, content, lines);
case 'markdown':
return this.parseMarkdown(filePath, content, lines);
default:
return this.parseAsPlainText(filePath, content);
}
}
static parseTypeScript(filePath, content, lines) {
const chunks = [];
let currentLine = 0;
// Parse imports at the top
const imports = this.extractImports(lines, /^import\s+.*?from\s+['"`].*?['"`]|^import\s+['"`].*?['"`]/);
if (imports.length > 0) {
chunks.push(this.createChunk(filePath, 'import', imports.join('\n'), 1, imports.length, 'imports'));
currentLine = imports.length;
}
// Parse interfaces and types
chunks.push(...this.parseTypeScriptInterfaces(filePath, lines));
chunks.push(...this.parseTypeScriptTypes(filePath, lines));
// Parse classes
chunks.push(...this.parseTypeScriptClasses(filePath, lines));
// Parse functions (including arrow functions)
chunks.push(...this.parseTypeScriptFunctions(filePath, lines));
// Parse enums
chunks.push(...this.parseTypeScriptEnums(filePath, lines));
// If no logical chunks found, return whole file
if (chunks.length === 0) {
chunks.push(this.createChunk(filePath, 'file', content, 1, lines.length, 'complete-file'));
}
return chunks;
}
static parseTypeScriptInterfaces(filePath, lines) {
const chunks = [];
const interfaceRegex = /^(export\s+)?interface\s+(\w+)/;
for (let i = 0; i < lines.length; i++) {
const match = lines[i].match(interfaceRegex);
if (match) {
const interfaceName = match[2];
const { endLine, content } = this.findBlockEnd(lines, i, '{', '}');
chunks.push(this.createChunk(filePath, 'interface', content, i + 1, endLine + 1, interfaceName, { exported: !!match[1] }));
i = endLine;
}
}
return chunks;
}
static parseTypeScriptTypes(filePath, lines) {
const chunks = [];
const typeRegex = /^(export\s+)?type\s+(\w+)\s*=/;
for (let i = 0; i < lines.length; i++) {
const match = lines[i].match(typeRegex);
if (match) {
const typeName = match[2];
// Type definitions can be single line or multi-line
let endLine = i;
let content = lines[i];
// Check if it's a multi-line type definition
if (lines[i].includes('{') && !lines[i].includes('}')) {
const result = this.findBlockEnd(lines, i, '{', '}');
endLine = result.endLine;
content = result.content;
}
chunks.push(this.createChunk(filePath, 'type', content, i + 1, endLine + 1, typeName, { exported: !!match[1] }));
i = endLine;
}
}
return chunks;
}
static parseTypeScriptClasses(filePath, lines) {
const chunks = [];
const classRegex = /^(export\s+)?(abstract\s+)?class\s+(\w+)/;
for (let i = 0; i < lines.length; i++) {
const match = lines[i].match(classRegex);
if (match) {
const className = match[3];
const { endLine, content } = this.findBlockEnd(lines, i, '{', '}');
chunks.push(this.createChunk(filePath, 'class', content, i + 1, endLine + 1, className, {
exported: !!match[1],
abstract: !!match[2]
}));
i = endLine;
}
}
return chunks;
}
static parseTypeScriptFunctions(filePath, lines) {
const chunks = [];
// Regular function declarations
const functionRegex = /^(export\s+)?(async\s+)?function\s+(\w+)/;
// Arrow function assignments
const arrowFunctionRegex = /^(export\s+)?const\s+(\w+)\s*=\s*(async\s+)?\([^)]*\)\s*=>/;
// Method definitions in objects
const methodRegex = /^\s*(\w+)\s*\([^)]*\)\s*{/;
for (let i = 0; i < lines.length; i++) {
let match = lines[i].match(functionRegex);
let functionName = '';
let isExported = false;
let isAsync = false;
if (match) {
functionName = match[3];
isExported = !!match[1];
isAsync = !!match[2];
}
else {
match = lines[i].match(arrowFunctionRegex);
if (match) {
functionName = match[2];
isExported = !!match[1];
isAsync = !!match[3];
}
}
if (match) {
const { endLine, content } = this.findBlockEnd(lines, i, '{', '}');
chunks.push(this.createChunk(filePath, 'function', content, i + 1, endLine + 1, functionName, {
exported: isExported,
async: isAsync
}));
i = endLine;
}
}
return chunks;
}
static parseTypeScriptEnums(filePath, lines) {
const chunks = [];
const enumRegex = /^(export\s+)?enum\s+(\w+)/;
for (let i = 0; i < lines.length; i++) {
const match = lines[i].match(enumRegex);
if (match) {
const enumName = match[2];
const { endLine, content } = this.findBlockEnd(lines, i, '{', '}');
chunks.push(this.createChunk(filePath, 'class', // Treat enums as classes for simplicity
content, i + 1, endLine + 1, enumName, {
exported: !!match[1],
type: 'enum'
}));
i = endLine;
}
}
return chunks;
}
static parsePython(filePath, content, lines) {
const chunks = [];
// Parse imports
const imports = this.extractImports(lines, /^(import\s+\w+|from\s+\w+\s+import)/);
if (imports.length > 0) {
chunks.push(this.createChunk(filePath, 'import', imports.join('\n'), 1, imports.length, 'imports'));
}
// Parse classes
const classRegex = /^class\s+(\w+)/;
for (let i = 0; i < lines.length; i++) {
const match = lines[i].match(classRegex);
if (match) {
const className = match[1];
const { endLine, content } = this.findPythonBlockEnd(lines, i);
chunks.push(this.createChunk(filePath, 'class', content, i + 1, endLine + 1, className));
i = endLine;
}
}
// Parse functions
const functionRegex = /^(async\s+)?def\s+(\w+)/;
for (let i = 0; i < lines.length; i++) {
const match = lines[i].match(functionRegex);
if (match) {
const functionName = match[2];
const { endLine, content } = this.findPythonBlockEnd(lines, i);
chunks.push(this.createChunk(filePath, 'function', content, i + 1, endLine + 1, functionName, { async: !!match[1] }));
i = endLine;
}
}
if (chunks.length === 0) {
chunks.push(this.createChunk(filePath, 'file', content, 1, lines.length, 'complete-file'));
}
return chunks;
}
static parseJava(filePath, content, lines) {
const chunks = [];
// Parse imports
const imports = this.extractImports(lines, /^import\s+/);
if (imports.length > 0) {
chunks.push(this.createChunk(filePath, 'import', imports.join('\n'), 1, imports.length, 'imports'));
}
// Parse classes
const classRegex = /^(public\s+)?(abstract\s+)?class\s+(\w+)/;
for (let i = 0; i < lines.length; i++) {
const match = lines[i].match(classRegex);
if (match) {
const className = match[3];
const { endLine, content } = this.findBlockEnd(lines, i, '{', '}');
chunks.push(this.createChunk(filePath, 'class', content, i + 1, endLine + 1, className, {
public: !!match[1],
abstract: !!match[2]
}));
i = endLine;
}
}
// Parse methods
const methodRegex = /^\s*(public|private|protected)?\s*(static\s+)?(\w+)\s+(\w+)\s*\(/;
for (let i = 0; i < lines.length; i++) {
const match = lines[i].match(methodRegex);
if (match && !lines[i].includes('class')) {
const methodName = match[4];
const { endLine, content } = this.findBlockEnd(lines, i, '{', '}');
chunks.push(this.createChunk(filePath, 'function', content, i + 1, endLine + 1, methodName, {
visibility: match[1] || 'package',
static: !!match[2],
returnType: match[3]
}));
i = endLine;
}
}
if (chunks.length === 0) {
chunks.push(this.createChunk(filePath, 'file', content, 1, lines.length, 'complete-file'));
}
return chunks;
}
static parseC(filePath, content, lines) {
const chunks = [];
// Parse includes
const includes = this.extractImports(lines, /^#include/);
if (includes.length > 0) {
chunks.push(this.createChunk(filePath, 'import', includes.join('\n'), 1, includes.length, 'includes'));
}
// Parse functions
const functionRegex = /^(\w+\s+)*(\w+)\s*\([^)]*\)\s*{/;
for (let i = 0; i < lines.length; i++) {
const match = lines[i].match(functionRegex);
if (match && !lines[i].includes('if') && !lines[i].includes('for') && !lines[i].includes('while')) {
const functionName = match[2];
const { endLine, content } = this.findBlockEnd(lines, i, '{', '}');
chunks.push(this.createChunk(filePath, 'function', content, i + 1, endLine + 1, functionName));
i = endLine;
}
}
if (chunks.length === 0) {
chunks.push(this.createChunk(filePath, 'file', content, 1, lines.length, 'complete-file'));
}
return chunks;
}
static parseGo(filePath, content, lines) {
const chunks = [];
// Parse imports
const imports = this.extractImports(lines, /^import\s+/);
if (imports.length > 0) {
chunks.push(this.createChunk(filePath, 'import', imports.join('\n'), 1, imports.length, 'imports'));
}
// Parse functions
const functionRegex = /^func\s+(\w+)\s*\(/;
for (let i = 0; i < lines.length; i++) {
const match = lines[i].match(functionRegex);
if (match) {
const functionName = match[1];
const { endLine, content } = this.findBlockEnd(lines, i, '{', '}');
chunks.push(this.createChunk(filePath, 'function', content, i + 1, endLine + 1, functionName));
i = endLine;
}
}
// Parse structs
const structRegex = /^type\s+(\w+)\s+struct/;
for (let i = 0; i < lines.length; i++) {
const match = lines[i].match(structRegex);
if (match) {
const structName = match[1];
const { endLine, content } = this.findBlockEnd(lines, i, '{', '}');
chunks.push(this.createChunk(filePath, 'class', content, i + 1, endLine + 1, structName, { type: 'struct' }));
i = endLine;
}
}
if (chunks.length === 0) {
chunks.push(this.createChunk(filePath, 'file', content, 1, lines.length, 'complete-file'));
}
return chunks;
}
static parseRust(filePath, content, lines) {
const chunks = [];
// Parse functions
const functionRegex = /^(pub\s+)?(async\s+)?fn\s+(\w+)/;
for (let i = 0; i < lines.length; i++) {
const match = lines[i].match(functionRegex);
if (match) {
const functionName = match[3];
const { endLine, content } = this.findBlockEnd(lines, i, '{', '}');
chunks.push(this.createChunk(filePath, 'function', content, i + 1, endLine + 1, functionName, {
public: !!match[1],
async: !!match[2]
}));
i = endLine;
}
}
// Parse structs
const structRegex = /^(pub\s+)?struct\s+(\w+)/;
for (let i = 0; i < lines.length; i++) {
const match = lines[i].match(structRegex);
if (match) {
const structName = match[2];
const { endLine, content } = this.findBlockEnd(lines, i, '{', '}');
chunks.push(this.createChunk(filePath, 'class', content, i + 1, endLine + 1, structName, {
public: !!match[1],
type: 'struct'
}));
i = endLine;
}
}
if (chunks.length === 0) {
chunks.push(this.createChunk(filePath, 'file', content, 1, lines.length, 'complete-file'));
}
return chunks;
}
static parseJSON(filePath, content, lines) {
// For JSON files, try to parse top-level objects/arrays as chunks
try {
const parsed = JSON.parse(content);
const chunks = [];
if (typeof parsed === 'object' && parsed !== null) {
if (Array.isArray(parsed)) {
chunks.push(this.createChunk(filePath, 'file', content, 1, lines.length, 'json-array'));
}
else {
// Split object into chunks by top-level keys
const keys = Object.keys(parsed);
if (keys.length <= 3) {
// Small object, keep as one chunk
chunks.push(this.createChunk(filePath, 'file', content, 1, lines.length, 'json-object'));
}
else {
// Large object, create chunk for whole file
chunks.push(this.createChunk(filePath, 'file', content, 1, lines.length, 'json-object'));
}
}
}
return chunks;
}
catch {
// Invalid JSON, treat as plain text
return this.parseAsPlainText(filePath, content);
}
}
static parseMarkdown(filePath, content, lines) {
const chunks = [];
// Parse markdown headers as chunks
let currentChunk = '';
let currentStart = 1;
let currentHeader = 'introduction';
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (line.startsWith('#')) {
// New header found
if (currentChunk.trim()) {
chunks.push(this.createChunk(filePath, 'comment', currentChunk.trim(), currentStart, i, currentHeader));
}
currentHeader = line.replace(/^#+\s*/, '').trim() || `header-${i + 1}`;
currentChunk = line + '\n';
currentStart = i + 1;
}
else {
currentChunk += line + '\n';
}
}
// Add final chunk
if (currentChunk.trim()) {
chunks.push(this.createChunk(filePath, 'comment', currentChunk.trim(), currentStart, lines.length, currentHeader));
}
if (chunks.length === 0) {
chunks.push(this.createChunk(filePath, 'file', content, 1, lines.length, 'markdown-content'));
}
return chunks;
}
static parseAsPlainText(filePath, content) {
const lines = content.split('\n');
// For small files (< 100 lines), keep as single chunk
if (lines.length < 100) {
return [this.createChunk(filePath, 'file', content, 1, lines.length, 'text-file')];
}
// For larger files, split into logical chunks of ~50 lines
const chunks = [];
const chunkSize = 50;
for (let i = 0; i < lines.length; i += chunkSize) {
const chunkLines = lines.slice(i, i + chunkSize);
const chunkContent = chunkLines.join('\n');
chunks.push(this.createChunk(filePath, 'file', chunkContent, i + 1, Math.min(i + chunkSize, lines.length), `chunk-${Math.floor(i / chunkSize) + 1}`));
}
return chunks;
}
static extractImports(lines, regex) {
const imports = [];
for (const line of lines) {
if (regex.test(line.trim())) {
imports.push(line);
}
else if (imports.length > 0 && !line.trim().startsWith('//') && line.trim() !== '') {
// Stop collecting imports when we hit non-import, non-comment code
break;
}
}
return imports;
}
static findBlockEnd(lines, startLine, openChar, closeChar) {
let depth = 0;
let endLine = startLine;
let foundOpen = false;
for (let i = startLine; i < lines.length; i++) {
const line = lines[i];
for (const char of line) {
if (char === openChar) {
depth++;
foundOpen = true;
}
else if (char === closeChar) {
depth--;
if (depth === 0 && foundOpen) {
endLine = i;
break;
}
}
}
if (depth === 0 && foundOpen) {
break;
}
}
const content = lines.slice(startLine, endLine + 1).join('\n');
return { endLine, content };
}
static findPythonBlockEnd(lines, startLine) {
let endLine = startLine;
const baseIndent = lines[startLine].match(/^\s*/)?.[0].length || 0;
for (let i = startLine + 1; i < lines.length; i++) {
const line = lines[i];
const indent = line.match(/^\s*/)?.[0].length || 0;
// Empty lines or comments are part of the block
if (line.trim() === '' || line.trim().startsWith('#')) {
endLine = i;
continue;
}
// If indentation is less than or equal to base, we've reached the end
if (indent <= baseIndent) {
break;
}
endLine = i;
}
const content = lines.slice(startLine, endLine + 1).join('\n');
return { endLine, content };
}
static createChunk(filePath, type, content, startLine, endLine, name, metadata = {}) {
const id = `${path.basename(filePath)}_${type}_${name}_${startLine}`;
return {
id: Buffer.from(id).toString('base64').substring(0, 16),
content: content.trim(),
filePath,
type,
name,
startLine,
endLine,
metadata: {
language: this.getLanguageFromPath(filePath),
size: content.length,
...metadata
}
};
}
static getLanguageFromPath(filePath) {
const ext = path.extname(filePath).toLowerCase();
return this.LANGUAGE_PARSERS[ext] || 'text';
}
}
exports.CodeParser = CodeParser;
CodeParser.LANGUAGE_PARSERS = {
'.ts': 'typescript',
'.tsx': 'typescript',
'.js': 'javascript',
'.jsx': 'javascript',
'.py': 'python',
'.java': 'java',
'.cpp': 'cpp',
'.c': 'c',
'.h': 'c',
'.go': 'go',
'.rs': 'rust',
'.php': 'php',
'.rb': 'ruby',
'.swift': 'swift',
'.kt': 'kotlin',
'.cs': 'csharp',
'.scala': 'scala',
'.clj': 'clojure',
'.sh': 'shell',
'.yml': 'yaml',
'.yaml': 'yaml',
'.json': 'json',
'.md': 'markdown'
};
//# sourceMappingURL=code-parser.js.map