mcard-js
Version:
MCard - Content-addressable storage with cryptographic hashing, handle resolution, and vector search for Node.js and browsers
148 lines • 5.69 kB
JavaScript
export class ProgrammingLanguageDetector {
contentTypeName = "code";
detect(contentSample, lines, firstLine, fileExtension) {
const mime = this.getMimeType(contentSample, lines, firstLine, fileExtension);
return (mime && mime !== 'text/plain') ? 0.95 : 0.0;
}
getMimeType(contentSample, lines, firstLine, fileExtension) {
const text = typeof contentSample === 'string' ? contentSample : new TextDecoder().decode(contentSample);
// 1. Python Detection
if (this.isPython(firstLine, text, lines)) {
return 'text/x-python';
}
// 2. C/C++ Detection
const cType = this.detectCFamily(text);
if (cType)
return cType;
// 3. JS/JSX Detection
const jsType = this.detectJsType(text);
if (jsType)
return jsType;
// 4. TypeScript Detection
if (this.isTypescript(text)) {
return 'text/typescript';
}
return 'text/plain';
}
isPython(firstLine, text, lines) {
// Imports
if (/^\s*import\s+(\w+|\w+\.\w+)/m.test(text) || /^\s*from\s+(\w+|\w+\.\w+)\s+import\s+/m.test(text)) {
const stdLibs = ['os', 'sys', 're', 'json', 'math', 'random', 'datetime'];
if (stdLibs.some(lib => text.includes(`import ${lib}`) || text.includes(`from ${lib}`))) {
return true;
}
}
// Shebang
if (firstLine.startsWith('#!') && firstLine.toLowerCase().includes('python'))
return true;
// Strong indicators
if (text.includes('if __name__ ==') && text.includes('__main__'))
return true;
if (/^\s*def\s+\w+\s*\(/.test(text) && !text.includes('function'))
return true;
if (/^\s*class\s+\w+\s*[\(:]/m.test(text))
return true;
if (/^\s*@\w+/m.test(text))
return true; // Decorator
// Pattern counting
let count = 0;
const patterns = [
/\bif\b.*?:/, /\belif\b.*?:/, /\belse\s*:/, /\bfor\b.*?\bin\b.*?:/,
/\bwhile\b.*?:/, /\btry\s*:/, /\bexcept\b.*?:/, /\bfinally\s*:/,
/\bNone\b/, /\bTrue\b/, /\bFalse\b/, /f["'].*?\{.*?\}["']/, // f-string
/\bdef\b/, /\bclass\b/, /\bimport\b/, /\bfrom\b/, /\blambda\b.*?:/
];
for (const p of patterns) {
if (p.test(text))
count++;
}
// Short content heuristic
const nonEmptyLines = lines.filter(l => l.trim().length > 0).length;
if (nonEmptyLines <= 5 && count >= 1) {
return true;
}
return count >= 3;
}
detectCFamily(text) {
// C/C++ patterns
const cPatterns = [
/#include\s*<.*?>/, /#include\s*".*?"/,
/\b(int|void|char|float|double)\s+main\s*\(.*\)\s*\{/,
/\bstruct\s+\w+\s*\{/, /#define\s+\w+/,
/printf\(.*?\);/, /scanf\(.*?\);/
];
const cppPatterns = [
/\bclass\s+\w+\s*\{/, /\bnamespace\s+\w+\s*\{/,
/\btemplate\s*<.*?>/, /::/, /\bstd::/,
/\bcout\s*<</, /\bcin\s*>>/,
/\bnew\s+\w+/, /\bdelete\s+\w+/,
/#include\s*<iostream>/
];
let cCount = 0;
let cppCount = 0;
cPatterns.forEach(p => { if (p.test(text))
cCount++; });
cppPatterns.forEach(p => { if (p.test(text))
cppCount++; });
if (cppCount >= 2 || (cppCount >= 1 && text.includes('std::')))
return 'text/x-c++';
if (cCount >= 2)
return 'text/x-c';
return null;
}
detectJsType(text) {
// JS patterns
const jsPatterns = [
/function\s+\w+\s*\(/.test(text), // function foo(
/\bconst\s+\w+\s*=/.test(text),
/\blet\s+\w+\s*=/.test(text),
/\bvar\s+\w+\s*=/.test(text),
/\bimport\s+.*\s+from/.test(text),
/\bexport\s+/.test(text),
/\=\>\s*\{/.test(text), // Arrow func
/console\.log\(/.test(text)
];
const jsxPatterns = [
/<\w+(>|\s+.*?>)[\s\S]*?<\/\w+>/m.test(text),
/<\w+\s+\/>/m.test(text),
/className=/.test(text),
/React\.createElement/.test(text)
];
const jsCount = jsPatterns.filter(Boolean).length;
const jsxCount = jsxPatterns.filter(Boolean).length;
if (jsxCount > 0 && (text.includes('import React') || text.includes('from "react"')))
return 'text/jsx';
if (jsxCount >= 2)
return 'text/jsx';
if (jsCount >= 2) {
// Avoid JSON misclassification
const stripped = text.trim();
if ((stripped.startsWith('{') && stripped.endsWith('}')) || (stripped.startsWith('[') && stripped.endsWith(']'))) {
try {
JSON.parse(text);
if (jsCount < 2)
return null;
}
catch { }
}
return 'text/javascript';
}
return null;
}
isTypescript(text) {
const tsPatterns = [
/:\s*(string|number|boolean|any|void|null|undefined)\b/,
/\binterface\s+\w+\s*\{/,
/\bclass\s+\w+\s+implements\s+\w+/,
/\btype\s+\w+\s*=/,
/\b(public|private|protected)\s+/,
/\bnamespace\s+\w+\s*\{/,
/<\w+>/ // Generics (simple check)
];
let count = 0;
tsPatterns.forEach(p => { if (p.test(text))
count++; });
return count >= 2;
}
}
//# sourceMappingURL=LanguageDetector.js.map