devibe
Version:
Intelligent repository cleanup with auto mode, AI learning, markdown consolidation, auto-consolidate workflow, context-aware classification, and cost optimization
116 lines • 3.57 kB
JavaScript
/**
* Markdown Scanner
*
* Discovers markdown files and extracts metadata.
*/
import fg from 'fast-glob';
import * as fs from 'fs/promises';
import * as path from 'path';
import matter from 'gray-matter';
export class MarkdownScanner {
/**
* Scan directory for markdown files
*/
async scan(options) {
const pattern = options.recursive ? '**/*.md' : '*.md';
const files = await fg(pattern, {
cwd: options.targetDirectory,
ignore: this.buildIgnorePatterns(options),
absolute: true,
dot: options.includeHidden
});
return Promise.all(files.map(filePath => this.analyzeFile(filePath, options.targetDirectory)));
}
/**
* Build ignore patterns for file scanning
*/
buildIgnorePatterns(options) {
const defaults = [
'**/node_modules/**',
'**/.git/**',
'**/dist/**',
'**/build/**',
'**/.devibe/**',
'**/.unvibe/**',
'**/coverage/**'
];
return [...defaults, ...options.excludePatterns];
}
/**
* Analyze a single markdown file
*/
async analyzeFile(filePath, baseDir) {
const stats = await fs.stat(filePath);
const content = await fs.readFile(filePath, 'utf-8');
const metadata = this.extractMetadata(content);
return {
path: filePath,
relativePath: path.relative(baseDir, filePath),
name: path.basename(filePath),
size: stats.size,
lastModified: stats.mtime,
content,
metadata
};
}
/**
* Extract metadata from markdown content
*/
extractMetadata(content) {
// Parse frontmatter
let frontMatter;
try {
const parsed = matter(content);
if (Object.keys(parsed.data).length > 0) {
frontMatter = parsed.data;
}
}
catch {
// No frontmatter or invalid
}
// Extract title
const title = frontMatter?.title || this.extractTitle(content) || 'Untitled';
// Extract headers
const headers = this.extractHeaders(content);
// Count elements
const wordCount = this.countWords(content);
const linkCount = (content.match(/\[.*?\]\(.*?\)/g) || []).length;
const codeBlockCount = (content.match(/```/g) || []).length / 2;
const imageCount = (content.match(/!\[.*?\]\(.*?\)/g) || []).length;
return {
title,
headers,
wordCount,
linkCount,
codeBlockCount,
imageCount,
frontMatter
};
}
/**
* Extract title from markdown content
*/
extractTitle(content) {
const match = content.match(/^#\s+(.+)$/m);
return match ? match[1].trim() : null;
}
/**
* Extract all headers from markdown content
*/
extractHeaders(content) {
const matches = content.matchAll(/^#{1,6}\s+(.+)$/gm);
return Array.from(matches, m => m[1].trim());
}
/**
* Count words in markdown content
*/
countWords(content) {
// Remove code blocks
const withoutCode = content.replace(/```[\s\S]*?```/g, '');
// Remove links
const withoutLinks = withoutCode.replace(/\[.*?\]\(.*?\)/g, '');
// Count words
return withoutLinks.split(/\s+/).filter(w => w.length > 0).length;
}
}
//# sourceMappingURL=markdown-scanner.js.map