~repos /website

#astro#js#html#css

git clone https://pyrossh.dev/repos/website.git

木 Personal website of pyrossh. Built with astrojs, shiki, vite.



src/utils/files.ts



import fs from "fs/promises";
import { extensions as folderExtensions } from "vscode-icons/src/iconsManifest/supportedFolders.ts";
import { extensions as fileExtensions } from "vscode-icons/src/iconsManifest/supportedExtensions.ts";
export interface FileNode {
name: string;
path: string;
size: number;
ext: string;
absolutePath: string;
isDirectory: boolean;
children?: FileNode[];
}
export const sortAll = (a: FileNode, b: FileNode): number => {
if (a.isDirectory && !b.isDirectory) return -1;
if (!a.isDirectory && b.isDirectory) return 1;
return a.name.localeCompare(b.name);
}
export const sortChildren = (nodes: FileNode[]) => {
for (const node of nodes) {
if (node.isDirectory && node.children) {
node.children.sort(sortAll);
sortChildren(node.children);
}
}
return nodes.sort(sortAll);
};
export const buildFileTree = (files: any[]): FileNode[] => {
const root: FileNode[] = [];
for (const file of files) {
const parts = file.name.split('/');
let currentLevel = root;
for (let i = 0; i < parts.length; i++) {
const part = parts[i];
const isLastPart = i === parts.length - 1;
const currentPath = parts.slice(0, i + 1).join('/');
let existingNode = currentLevel.find(node => node.name === part);
if (!existingNode) {
const newNode: FileNode = {
name: part,
path: currentPath,
isDirectory: !isLastPart,
size: file.size,
ext: file.ext,
absolutePath: file.absolutePath,
};
if (!isLastPart) {
// It's a directory
newNode.children = [];
}
currentLevel.push(newNode);
existingNode = newNode;
}
if (!isLastPart) {
currentLevel = existingNode.children!;
}
}
}
return root;
}
export const checkFileExists = async (filePath: string) => {
try {
await fs.stat(filePath);
return true; // File exists
} catch (error) {
return false; // File does not exist or cannot be accessed
}
}
export const resolveFolderIcon = (foldername: string) => {
for (const item of folderExtensions.supported) {
if (item.extensions?.includes(foldername)) {
return item.icon;
}
}
return folderExtensions.default.folder?.icon;
}
export const resolveFileIcon = (filename: string, fileExt: string) => {
const baseName = filename.split('/').pop() || filename;
// First check for exact filename matches
for (const item of fileExtensions.supported) {
if (item.filename && item.extensions?.includes(baseName)) {
return item.icon;
}
}
// Then check for extension matches (more specific than language extensions)
for (const item of fileExtensions.supported) {
if (item.extensions?.includes(fileExt)) {
return item.icon;
}
}
// Check for language-specific extensions, prioritizing exact language ID matches
for (const item of fileExtensions.supported) {
for (const lang of item.languages ?? []) {
if (lang?.knownExtensions?.includes(fileExt)) {
const langIds = Array.isArray(lang.ids) ? lang.ids : [lang.ids];
if (langIds.includes(fileExt)) {
return item.icon;
}
}
}
}
// Fallback to any language extension match
for (const item of fileExtensions.supported) {
for (const lang of item.languages ?? []) {
if (lang?.knownExtensions?.includes(fileExt)) {
return item.icon;
}
}
}
return fileExtensions.default.file?.icon;
}