@felixgeelhaar/cclint
Version:
A comprehensive linter for CLAUDE.md files with multi-language code block validation
202 lines • 5.76 kB
JavaScript
import { Location } from './Location.js';
/**
* Represents a code block found within a CLAUDE.md file
*/
export class CodeBlock {
language;
content;
location;
context;
isComplete;
hasImports;
metadata;
constructor(language, content, location, context = '', metadata) {
this.language = this.normalizeLanguage(language);
this.content = content;
this.location = location;
this.context = context;
this.isComplete = this.checkCompleteness();
this.hasImports = this.checkImports();
this.metadata = {
indentLevel: metadata?.indentLevel ?? 0,
isExample: metadata?.isExample ?? true,
isAntiPattern: metadata?.isAntiPattern ?? false,
description: metadata?.description ?? '',
...metadata,
};
}
/**
* Get the lines of code as an array
*/
getLines() {
return this.content.split('\n');
}
/**
* Get the number of lines in the code block
*/
getLineCount() {
return this.getLines().length;
}
/**
* Check if this code block appears to be complete and runnable
*/
checkCompleteness() {
const lines = this.getLines();
// Empty blocks are not complete
if (lines.length === 0 || this.content.trim() === '') {
return false;
}
// Check for common incomplete patterns
const incompletePatterns = [
/^\s*\.\.\.\s*$/, // Just ellipsis
/\/\/\s*\.\.\.\s*$/, // Comment with ellipsis
/#\s*\.\.\.\s*$/, // Python comment with ellipsis
/^\s*\/\/\s*TODO/i, // TODO comment
/^\s*#\s*TODO/i, // Python TODO
/^\s*\/\/\s*more code here/i, // Placeholder comment
];
for (const line of lines) {
for (const pattern of incompletePatterns) {
if (pattern.test(line)) {
return false;
}
}
}
return true;
}
/**
* Check if the code block contains import statements
*/
checkImports() {
const importPatterns = [
/^import\s+/m, // ES6 imports
/^from\s+\S+\s+import\s+/m, // Python imports
/^import\s+\(/m, // Go imports
/^require\s*\(/m, // CommonJS
/^using\s+/m, // C#
/^#include\s+/m, // C/C++
/^use\s+/m, // Rust/PHP
];
for (const pattern of importPatterns) {
if (pattern.test(this.content)) {
return true;
}
}
return false;
}
/**
* Normalize language identifiers to standard names
*/
normalizeLanguage(language) {
const normalized = language.toLowerCase().trim();
// Map common aliases to standard names
const languageMap = {
js: 'javascript',
ts: 'typescript',
tsx: 'typescriptreact',
jsx: 'javascriptreact',
py: 'python',
rb: 'ruby',
yml: 'yaml',
shell: 'bash',
sh: 'bash',
zsh: 'bash',
golang: 'go',
'c++': 'cpp',
'c#': 'csharp',
'objective-c': 'objc',
'f#': 'fsharp',
};
return languageMap[normalized] || normalized;
}
/**
* Get a display name for the language
*/
getLanguageDisplayName() {
const displayNames = {
javascript: 'JavaScript',
typescript: 'TypeScript',
typescriptreact: 'TypeScript (TSX)',
javascriptreact: 'JavaScript (JSX)',
python: 'Python',
go: 'Go',
rust: 'Rust',
java: 'Java',
csharp: 'C#',
cpp: 'C++',
ruby: 'Ruby',
php: 'PHP',
swift: 'Swift',
kotlin: 'Kotlin',
scala: 'Scala',
bash: 'Bash',
sql: 'SQL',
yaml: 'YAML',
json: 'JSON',
html: 'HTML',
css: 'CSS',
markdown: 'Markdown',
};
return displayNames[this.language] || this.language;
}
/**
* Check if this is a supported programming language
*/
isSupportedLanguage() {
const supportedLanguages = new Set([
'javascript',
'typescript',
'typescriptreact',
'javascriptreact',
'python',
'go',
'rust',
'java',
'csharp',
'cpp',
'c',
'ruby',
'php',
'swift',
'kotlin',
'scala',
'bash',
'sql',
'yaml',
'json',
]);
return supportedLanguages.has(this.language);
}
/**
* Get file extension for this language
*/
getFileExtension() {
const extensions = {
javascript: '.js',
typescript: '.ts',
typescriptreact: '.tsx',
javascriptreact: '.jsx',
python: '.py',
go: '.go',
rust: '.rs',
java: '.java',
csharp: '.cs',
cpp: '.cpp',
c: '.c',
ruby: '.rb',
php: '.php',
swift: '.swift',
kotlin: '.kt',
scala: '.scala',
bash: '.sh',
sql: '.sql',
yaml: '.yaml',
json: '.json',
html: '.html',
css: '.css',
markdown: '.md',
};
return extensions[this.language] || `.${this.language}`;
}
}
//# sourceMappingURL=CodeBlock.js.map