feat: Add Tailwind CSS and navigation generation
- 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.
This commit is contained in:
137
sweb/scripts/generateNavData.ts
Normal file
137
sweb/scripts/generateNavData.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
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();
|
Reference in New Issue
Block a user