react 资源清单

避免资源文件硬编码,和后期清理维护问题

TypeScript 复制代码
import fs from 'fs';
import path from 'path';
import chokidar from 'chokidar';

const headerNotice = "/**" +
    " * THIS FILE IS AUTO-GENERATED\n" +
    " * --------------------------------------------------------------------------\n" +
    " *\n" +
    " * ⚠️ WARNING:\n" +
    " *   This file is automatically generated by scripts/code-gen.\n" +
    " *   Do NOT modify this file manually, as your changes will be overwritten.\n" +
    " *\n" +
    " * INFO:\n" +
    " *   Author: XXF\n" +
    " *   Generator: bun run r\n" +
    " *   Purpose: Provide static access to resources (images, icons, etc.)\n" +
    " *\n" +
    " * USAGE:\n" +
    " *   import { R } from '@/lib/code-gen/output/R';\n" +
    " *   <img src={R.images.icons.close} alt=\"close\" />\n" +
    " *\n" +
    " * --------------------------------------------------------------------------\n" +
    " */"
/// 执行脚本  bun lib/code-gen/scripts/generateR.ts --watch
/// 或者 bun run r
const resourceRoot = path.resolve(__dirname, '../../../public');
const outDir = path.resolve(__dirname, '../output');
const outFile = path.join(outDir, 'R.ts');
const outDtsFile = path.join(outDir, 'RType.d.ts');

const allowedExt = ['.png', '.jpg', '.jpeg', '.svg', '.gif', '.webp','.ico'];

// 转换文件名为合法 TS key
const toKey = (name: string) => name.replace(/[-\s]/g, '_').replace(/\.[^/.]+$/, '');

// 递归遍历目录生成对象
function walkDir(dir: string, basePath = ''): Record<string, any> {
    const entries = fs.readdirSync(dir, {withFileTypes: true});
    const result: Record<string, any> = {};
    for (const entry of entries) {
        const fullPath = path.join(dir, entry.name);
        const relativePath = path.join(basePath, entry.name);
        if (entry.isDirectory()) {
            result[toKey(entry.name)] = walkDir(fullPath, relativePath);
        } else if (entry.isFile()) {
            const ext = path.extname(entry.name).toLowerCase();
            if (allowedExt.includes(ext)) {
                result[toKey(entry.name)] = '/' + relativePath.replace(/\\/g, '/');
            }
        }
    }
    return result;
}

// 生成 TS 文件
function generateTS(obj: Record<string, any>, className = 'R', rootKey = 'images') {
    const lines = [`${headerNotice}\n`, `export class ${className} {`];
    lines.push(`  static ${rootKey} = ${JSON.stringify(obj, null, 2)};`);
    lines.push('}\n');
    return lines.join('\n');
}

// 生成 d.ts 文件
function generateDTS(obj: Record<string, any>, className = 'R', rootKey = 'images') {
    function buildType(obj: any): string {
        const lines: string[] = ['{'];
        for (const key in obj) {
            if (typeof obj[key] === 'string') {
                lines.push(`  ${key}: string;`);
            } else {
                lines.push(`  ${key}: ${buildType(obj[key])}`);
            }
        }
        lines.push('}');
        return lines.join('\n');
    }

    return `${headerNotice}\n` + `export declare class ${className} {\n  static ${rootKey}: ${buildType(obj)};\n}`;
}

// 执行生成
function generateAll() {
    if (!fs.existsSync(resourceRoot)) {
        console.log("resourceRoot is not exist");
        return;
    }
    const rObject = walkDir(resourceRoot);
    if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, {recursive: true});
    fs.writeFileSync(outFile, generateTS(rObject), 'utf-8');
    fs.writeFileSync(outDtsFile, generateDTS(rObject), 'utf-8');
    console.log('✅ generateR.ts and R.d.ts generated successfully!');
}

// 支持 watch 模式
if (process.argv.includes('--watch')) {
    console.log('Watching public/images for changes...');
    generateAll();
    chokidar.watch(resourceRoot, {ignoreInitial: true, depth: 10}).on('all', () => generateAll());
} else {
    generateAll();
}

生成文件如下

业务可以直接应用

TypeScript 复制代码
  import { R } from '@/lib/code-gen/output/R';
  <img src={R.images.icons.close} alt="close" />
相关推荐
子兮曰1 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰1 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万1 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝1 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋1 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁1 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
李少兄1 天前
JavaScript 隐式全局变量解析
javascript
汉堡大王95271 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大1 天前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端
计算机魔术师1 天前
Meta Muse agent 接入 Shopify 的 Shop Pay 实现代理式购物
前端