simple-file-site
Version:
A simple file list site generator
156 lines (126 loc) • 4.75 kB
JavaScript
import fs from "fs-extra";
import ejs from "ejs";
import path from "path";
import { fileURLToPath } from "url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
class FileIndexGenerator {
static getFileIcon(filename, isDirectory) {
if (isDirectory) return "📁";
return filename.match(/\.(jpg|jpeg|png|gif|webp)$/i) ? "🖼️" : "📄";
}
static getFileSize(size) {
if (size < 1024) return `${size} B`;
if (size < 1024 * 1024) return `${(size / 1024).toFixed(2)} KB`;
if (size < 1024 * 1024 * 1024) return `${(size / (1024 * 1024)).toFixed(2)} MB`;
return `${(size / (1024 * 1024 * 1024)).toFixed(2)} GB`;
}
static async processEntry(entryPath, entry) {
const stats = await fs.stat(entryPath);
const isDirectory = stats.isDirectory();
const size = isDirectory ? "-" : FileIndexGenerator.getFileSize(stats.size);
const icon = FileIndexGenerator.getFileIcon(entry, isDirectory);
return {
name: entry,
icon,
size,
isDirectory,
};
}
constructor(fromDir = "source", toDir = "public") {
this.fromDir = fromDir;
this.toDir = toDir;
this.config = {
footerLink: {
text: "GitHub",
url: "https://github.com/adogecheems/simple-file-site",
},
};
}
async initialize() {
try {
await fs.remove(this.toDir);
await fs.copy(this.fromDir, this.toDir);
} catch (error) {
throw new Error(`初始化目录失败: ${error.message}`);
}
}
async generateHtml(dir) {
try {
const entries = await fs.readdir(dir);
const items = { folders: [], files: [] };
if (dir !== this.toDir) {
items.folders.push({
name: "..",
icon: "📁",
size: "-",
isDirectory: true,
});
}
for (const entry of entries) {
const entryPath = path.resolve(dir, entry);
const item = await FileIndexGenerator.processEntry(entryPath, entry);
items[item.isDirectory ? "folders" : "files"].push(item);
}
const templatePath = path.resolve(__dirname, "template", "index.ejs");
const template = await fs.readFile(templatePath, "utf-8");
const objs = [...items.folders, ...items.files];
const content = ejs.render(template, { objs, dir, config: this.config });
await fs.writeFile(path.resolve(dir, "index.html"), content);
console.log(`✅ 生成索引页面: ${dir}`);
} catch (error) {
throw new Error(`生成索引页面失败: ${error.message}`);
}
}
async processDir(dir) {
try {
const entries = await fs.readdir(dir);
await Promise.all(
entries.map(async (entry) => {
const entryPath = path.join(dir, entry);
const stats = await fs.stat(entryPath);
if (stats.isDirectory()) {
await this.processDir(entryPath);
}
})
);
await this.generateHtml(dir);
} catch (error) {
console.error(`❌ 处理目录失败 ${dir}:`, error);
}
}
async generate() {
try {
await this.initialize();
await this.processDir(this.toDir);
console.log("✨ 所有索引页面生成完成");
} catch (error) {
console.error(`❌ 生成索引页面失败: ${error.message}`);
}
}
}
const [fromDir, toDir] = process.argv.slice(2);
const generator = new FileIndexGenerator(fromDir, toDir);
await generator.generate();
(async () => {
try {
await fs.copy(path.resolve(__dirname, "_headers"),
path.resolve(generator.toDir, "_headers"),
{ overwrite: true }
);
if (!await fs.exists(path.join(__dirname, generator.toDir))) {
await fs.copy(
path.resolve(__dirname, "netlify.toml"),
path.resolve(generator.toDir, "..", "netlify.toml"),
{ overwrite: false }
);
await fs.copy(
path.resolve(__dirname, "vercel.json"),
path.resolve(generator.toDir, "..", "vercel.json"),
{ overwrite: false }
);
}
} catch (error) {
console.error(`❌ 后处理失败: ${error.message}`);
}
})();