Initial commit: 小羚羚小程序 Axhub Make workspace.
Include xll-miniapp prototype, PRD resources, annotation directory sync, agent skills, and cloud publishing setup. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
113
vite-plugins/injectStablePageIds.ts
Normal file
113
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