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>
This commit is contained in:
113
axhub-make/vite-plugins/injectStablePageIds.ts
Normal file
113
axhub-make/vite-plugins/injectStablePageIds.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
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
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user