- Added Tailwind CSS for styling. - Implemented automatic navigation data generation from markdown files. - Created `NavItem` component for rendering navigation items. - Added scripts for navigation generation and updated build process.
138 lines
3.8 KiB
TypeScript
138 lines
3.8 KiB
TypeScript
import fs from 'fs';
|
|
import path from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
|
|
// Define the NavItem interface to match the existing type
|
|
interface NavItem {
|
|
label: string;
|
|
link: string;
|
|
children?: NavItem[];
|
|
}
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
|
|
const docsDir = path.resolve(__dirname, '../src/docs');
|
|
const outputFile = path.resolve(__dirname, '../src/data/navData.json');
|
|
|
|
function toTitleCase(str: string): string {
|
|
return str
|
|
.replace(/[-_]/g, ' ')
|
|
.split(' ')
|
|
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
.join(' ');
|
|
}
|
|
|
|
function getBaseName(filePath: string): string {
|
|
return path.basename(filePath, path.extname(filePath));
|
|
}
|
|
|
|
function isHidden(name: string): boolean {
|
|
return name.startsWith('.');
|
|
}
|
|
|
|
/**
|
|
* Recursively checks if any markdown file exists in a directory or its subdirectories
|
|
*/
|
|
function hasMarkdownFilesDeep(dirPath: string): boolean {
|
|
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
|
|
for (const entry of entries) {
|
|
const fullPath = path.join(dirPath, entry.name);
|
|
if (isHidden(entry.name)) continue;
|
|
|
|
if (entry.isFile() && path.extname(entry.name).toLowerCase() === '.md') {
|
|
return true;
|
|
} else if (entry.isDirectory()) {
|
|
if (hasMarkdownFilesDeep(fullPath)) return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Recursively scans a directory and builds a navigation structure
|
|
*/
|
|
function scanDirectory(dirPath: string, relativePath: string = ''): NavItem[] {
|
|
const items: NavItem[] = [];
|
|
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
|
|
const directories = entries.filter(entry => entry.isDirectory() && !isHidden(entry.name));
|
|
for (const dir of directories) {
|
|
const dirRelativePath = path.join(relativePath, dir.name);
|
|
const dirFullPath = path.join(dirPath, dir.name);
|
|
|
|
if (!hasMarkdownFilesDeep(dirFullPath)) {
|
|
continue; // Skip folders that don't contain markdown files even in subdirs
|
|
}
|
|
|
|
const navItem: NavItem = {
|
|
label: toTitleCase(dir.name),
|
|
link: '/' + dirRelativePath,
|
|
children: scanDirectory(dirFullPath, dirRelativePath)
|
|
};
|
|
|
|
items.push(navItem);
|
|
}
|
|
|
|
const files = entries.filter(entry =>
|
|
entry.isFile() &&
|
|
!isHidden(entry.name) &&
|
|
path.extname(entry.name).toLowerCase() === '.md'
|
|
);
|
|
|
|
for (const file of files) {
|
|
const baseName = getBaseName(file.name);
|
|
const fileRelativePath = path.join(relativePath, baseName);
|
|
|
|
const navItem: NavItem = {
|
|
label: toTitleCase(baseName),
|
|
link: '/' + fileRelativePath
|
|
};
|
|
|
|
items.push(navItem);
|
|
}
|
|
|
|
return items;
|
|
}
|
|
|
|
function generateNavData(): void {
|
|
try {
|
|
console.log('Generating navigation data...');
|
|
|
|
const topLevelDirs = fs.readdirSync(docsDir, { withFileTypes: true })
|
|
.filter(entry => entry.isDirectory() && !isHidden(entry.name));
|
|
|
|
const navData: NavItem[] = [];
|
|
|
|
for (const dir of topLevelDirs) {
|
|
const dirPath = path.join(docsDir, dir.name);
|
|
|
|
if (!hasMarkdownFilesDeep(dirPath)) {
|
|
continue;
|
|
}
|
|
|
|
const children = scanDirectory(dirPath, dir.name);
|
|
|
|
const navItem: NavItem = {
|
|
label: toTitleCase(dir.name),
|
|
link: '/' + dir.name,
|
|
children
|
|
};
|
|
|
|
navData.push(navItem);
|
|
}
|
|
|
|
fs.writeFileSync(outputFile, JSON.stringify(navData, null, 2));
|
|
|
|
console.log(`Navigation data generated successfully: ${outputFile}`);
|
|
} catch (error) {
|
|
console.error('Error generating navigation data:', error);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// Run the generator
|
|
generateNavData();
|