Optimized the root .gitignore to exclude virtual environments, node modules, and temp folders to ensure clean and lightweight version tracking. Co-authored-by: Cursor <cursoragent@cursor.com>
114 lines
4.0 KiB
TypeScript
114 lines
4.0 KiB
TypeScript
import type { Plugin } from 'vite';
|
||
import { createHash } from 'crypto';
|
||
import path from 'path';
|
||
|
||
/**
|
||
* 为每个页面/组件文件注入稳定的唯一 ID
|
||
* 基于文件相对路径生成,保证构建间稳定不变
|
||
*
|
||
* 生成的 ID 格式:{项目名}-{目录类型}-{项目名称}-{16位哈希}
|
||
* 例如:axhub-make-prototypes-demo-antd-a1b2c3d4e5f6g7h8
|
||
*
|
||
* 规则:
|
||
* - 忽略 index.tsx 中的 index 部分(因为都是同一个,没有意义)
|
||
* - 使用项目目录名(如 axhub-make)
|
||
* - 根据 components 和 prototypes 目录区分
|
||
* - 加上文件项目名称
|
||
*/
|
||
export function injectStablePageIds(): Plugin {
|
||
const cwd = process.cwd();
|
||
|
||
// 从 cwd 获取项目目录名(如 axhub-make)
|
||
const projectName = path.basename(cwd);
|
||
|
||
return {
|
||
name: 'inject-stable-page-ids',
|
||
enforce: 'pre',
|
||
|
||
transform(code, id) {
|
||
// 只处理 tsx/jsx 文件
|
||
if (!/\.(tsx|jsx)$/.test(id)) return null;
|
||
|
||
// 获取相对路径
|
||
const relativePath = path.relative(cwd, id).replace(/\\/g, '/');
|
||
|
||
// 生成长哈希(16位 SHA-256,极低重复风险)
|
||
const longHash = createHash('sha256')
|
||
.update(relativePath)
|
||
.digest('hex')
|
||
.slice(0, 16);
|
||
|
||
// 解析路径:src/prototypes/demo-antd/index.tsx 或 src/components/demo-button/index.tsx
|
||
// 目标格式:axhub-make-prototypes-demo-antd 或 axhub-make-components-demo-button
|
||
// 规则:忽略 index.tsx 中的 index 部分,使用目录名作为项目名称
|
||
const pathParts = relativePath
|
||
.replace(/^src\//, '') // 移除 src/ 前缀
|
||
.replace(/\.(tsx|jsx)$/, '') // 移除文件扩展名
|
||
.split('/');
|
||
|
||
// 查找 prototypes 或 components 目录
|
||
const categoryIndex = pathParts.findIndex(part => part === 'prototypes' || part === 'components');
|
||
|
||
let readableId: string;
|
||
|
||
if (categoryIndex >= 0 && categoryIndex < pathParts.length - 1) {
|
||
// 找到目录类型(prototypes 或 components)
|
||
const category = pathParts[categoryIndex];
|
||
// 获取项目名称(category 后面的第一个非 index 部分)
|
||
// 例如:prototypes/demo-antd/index -> demo-antd
|
||
// prototypes/demo-antd/some-file -> demo-antd
|
||
// prototypes/index -> '' (空,使用降级方案)
|
||
let itemName = '';
|
||
|
||
// 从 category 后面开始查找项目名称
|
||
for (let i = categoryIndex + 1; i < pathParts.length; i++) {
|
||
const part = pathParts[i];
|
||
// 如果遇到 index,跳过它,继续查找下一级
|
||
if (part === 'index') {
|
||
continue;
|
||
}
|
||
// 找到第一个非 index 的部分,作为项目名称
|
||
itemName = part;
|
||
break;
|
||
}
|
||
|
||
// 组合:项目名-目录类型-项目名称
|
||
readableId = `${projectName}-${category}${itemName ? '-' + itemName : ''}`;
|
||
} else {
|
||
// 降级方案:如果路径不符合预期,使用原来的逻辑但移除 index
|
||
readableId = relativePath
|
||
.replace(/^src\//, '')
|
||
.replace(/\.(tsx|jsx)$/, '')
|
||
.replace(/\/index$/, '') // 移除末尾的 /index
|
||
.replace(/[\/\.]/g, '-')
|
||
.replace(/^-+|-+$/g, '') // 移除首尾的连字符
|
||
.slice(0, 48);
|
||
|
||
// 如果 readableId 不以项目名开头,则添加
|
||
if (!readableId.startsWith(projectName)) {
|
||
readableId = `${projectName}-${readableId}`;
|
||
}
|
||
}
|
||
|
||
// 组合 ID:可读路径 + 哈希
|
||
const stableId = `${readableId}-${longHash}`;
|
||
|
||
// 注入全局常量,组件中可直接使用
|
||
const injectedCode = `
|
||
// Auto-injected by vite-plugin-inject-stable-page-ids
|
||
const __PAGE_ID__ = '${stableId}';
|
||
const __PAGE_PATH__ = '${readableId}';
|
||
const __PAGE_FULL_PATH__ = '${relativePath}';
|
||
const __PAGE_HASH__ = '${longHash}';
|
||
|
||
${code}
|
||
`.trim();
|
||
|
||
return {
|
||
code: injectedCode,
|
||
map: null
|
||
};
|
||
}
|
||
};
|
||
}
|