Files
ONE-OS/axhub-make/vite-plugins/injectStablePageIds.ts
王冕 a27e3b8e43 feat: sync full workspace including web modules, docs, and configurations to Gitea
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>
2026-06-09 18:12:25 +08:00

114 lines
4.0 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
};
}
};
}