mirror of
https://github.com/ant-design/ant-design.git
synced 2026-02-09 02:49:18 +08:00
* chore: init link * docs: init * chore: adjust logic * chore: theme connect * chore: add prompt talk * chore: support chat prompt * chore: api call * chore: sse * feat: site alg * chore: init layer * chore: fix order * chore: update ts def * chore: tmp of it * chore: init script * chore: add generate script * chore: fix script * chore: llms semantic * chore: update desc * chore: fix lint * chore: fix lint
226 lines
7.3 KiB
TypeScript
226 lines
7.3 KiB
TypeScript
// Note: Generated By Copilot
|
||
|
||
import path from 'path';
|
||
import fs from 'fs-extra';
|
||
import { glob } from 'glob';
|
||
|
||
// 特殊组件名转换映射
|
||
const ConvertMap: Record<string, string> = {
|
||
'badge:ribbon': 'ribbon',
|
||
'floatButton:group': 'floatButtonGroup',
|
||
'input:input': 'input',
|
||
'input:otp': 'otp',
|
||
'input:search': 'inputSearch',
|
||
'input:textarea': 'textArea',
|
||
};
|
||
|
||
// 将 kebab-case 转换为 camelCase
|
||
function toCamelCase(str: string): string {
|
||
return str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
||
}
|
||
|
||
// 构建嵌套结构的辅助函数
|
||
function buildNestedStructure(flatSemantics: Record<string, string>): any {
|
||
const result: any = {};
|
||
|
||
// 首先,收集所有带点号的键,确定哪些是父级
|
||
const nestedKeys = new Set<string>();
|
||
for (const key of Object.keys(flatSemantics)) {
|
||
if (key.includes('.')) {
|
||
const parentKey = key.split('.')[0];
|
||
nestedKeys.add(parentKey);
|
||
}
|
||
}
|
||
|
||
for (const [key, value] of Object.entries(flatSemantics)) {
|
||
if (key.includes('.')) {
|
||
const parts = key.split('.');
|
||
let current = result;
|
||
|
||
// 遍历除最后一个部分外的所有部分
|
||
for (let i = 0; i < parts.length - 1; i++) {
|
||
const part = parts[i];
|
||
if (!current[part]) {
|
||
current[part] = {};
|
||
} else if (typeof current[part] === 'string') {
|
||
// 如果已经存在一个字符串值,需要将其转换为对象
|
||
// 保留原有的值作为一个特殊的 _value 属性或者忽略冲突
|
||
// console.warn(
|
||
// `Warning: Key conflict for '${part}', nested structure will override simple value`,
|
||
// );
|
||
current[part] = {};
|
||
}
|
||
current = current[part];
|
||
}
|
||
|
||
// 设置最后一个部分的值
|
||
current[parts[parts.length - 1]] = value;
|
||
} else {
|
||
// 只有当这个键不是某个嵌套结构的父级时,才直接设置值
|
||
if (!nestedKeys.has(key)) {
|
||
result[key] = value;
|
||
} else {
|
||
// 如果这个键将成为父级,先确保它是一个对象
|
||
if (!result[key]) {
|
||
result[key] = {};
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
// 递归生成 markdown 格式的嵌套结构
|
||
function generateMarkdownStructure(obj: any, prefix = '', indent = 0): string {
|
||
let result = '';
|
||
for (const [key, value] of Object.entries(obj)) {
|
||
const indentStr = ' '.repeat(indent);
|
||
if (typeof value === 'string') {
|
||
result += `${indentStr}- \`${key}\`: ${value}\n`;
|
||
} else {
|
||
result += `${indentStr}- \`${key}\`:\n`;
|
||
result += generateMarkdownStructure(value, prefix, indent + 1);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
// 递归打印嵌套结构的辅助函数(已注释掉)
|
||
// function printNestedStructure(obj: any, prefix = '', indent = 0): void {
|
||
// for (const [key, value] of Object.entries(obj)) {
|
||
// const indentStr = ' '.repeat(indent);
|
||
// if (typeof value === 'string') {
|
||
// console.log(`${indentStr}- ${key}: ${value}`);
|
||
// } else {
|
||
// console.log(`${indentStr}- ${key}:`);
|
||
// printNestedStructure(value, prefix, indent + 1);
|
||
// }
|
||
// }
|
||
// }
|
||
|
||
async function generateSemanticDesc() {
|
||
const cwd = process.cwd();
|
||
const siteDir = path.resolve(cwd, '_site');
|
||
const docsDir = ['components', 'docs'];
|
||
|
||
// Ensure siteDir
|
||
await fs.ensureDir(siteDir);
|
||
|
||
const docs = await glob(`{${docsDir.join(',')}}/**/demo/_semantic*.tsx`);
|
||
|
||
// Read `docs` file and generate semantic description.e.g.
|
||
// components/float-button/demo/_semantic_group.tsx is to:
|
||
// - root: 根元素
|
||
// - list: 列表元素
|
||
// - item: 列表项元素
|
||
// - root: 列表项根元素
|
||
// - icon: 列表项图标元素
|
||
// - content: 列表项内容元素
|
||
// - trigger:
|
||
// - root: 触发元素
|
||
// - icon: 触发图标元素
|
||
// - content: 触发内容元素
|
||
|
||
const semanticDescriptions: Record<string, any> = {};
|
||
|
||
for (const docPath of docs) {
|
||
try {
|
||
// 读取文件内容
|
||
const content = await fs.readFile(docPath, 'utf-8');
|
||
|
||
// 提取组件名称(从路径中获取)
|
||
const componentName = path.basename(path.dirname(path.dirname(docPath)));
|
||
const fileName = path.basename(docPath, '.tsx');
|
||
|
||
// 转换为 camelCase
|
||
const camelCaseComponentName = toCamelCase(componentName);
|
||
|
||
// 如果是 _semantic.tsx,使用组件名;如果是其他变体,添加后缀
|
||
let semanticKey = camelCaseComponentName;
|
||
if (fileName !== '_semantic') {
|
||
const variant = fileName.replace('_semantic_', '').replace('_semantic', '');
|
||
if (variant) {
|
||
semanticKey = `${camelCaseComponentName}:${variant}`;
|
||
}
|
||
}
|
||
|
||
// 检查是否有特殊转换映射
|
||
if (ConvertMap[semanticKey]) {
|
||
semanticKey = ConvertMap[semanticKey];
|
||
}
|
||
|
||
// 使用正则表达式提取 locales 对象
|
||
const localesMatch = content.match(/const locales = \{([\s\S]*?)\};/);
|
||
if (!localesMatch) {
|
||
// console.warn(`No locales found in ${docPath}`);
|
||
continue;
|
||
}
|
||
|
||
// 提取中文 locales
|
||
const cnMatch = content.match(/cn:\s*\{([\s\S]*?)\},?\s*en:/);
|
||
if (!cnMatch) {
|
||
// console.warn(`No Chinese locales found in ${docPath}`);
|
||
continue;
|
||
}
|
||
|
||
const cnContent = cnMatch[1];
|
||
const flatSemantics: Record<string, string> = {};
|
||
|
||
// 提取每个语义描述
|
||
const semanticMatches = cnContent.matchAll(/['"]?([^'":\s]+)['"]?\s*:\s*['"]([^'"]+)['"],?/g);
|
||
for (const match of semanticMatches) {
|
||
const [, key, value] = match;
|
||
flatSemantics[key] = value;
|
||
}
|
||
|
||
if (Object.keys(flatSemantics).length > 0) {
|
||
// 将扁平的语义描述转换为层级结构
|
||
const nestedSemantics = buildNestedStructure(flatSemantics);
|
||
semanticDescriptions[semanticKey] = nestedSemantics;
|
||
|
||
// 生成层级结构的描述(已注释掉)
|
||
// console.log(`\n${semanticKey}:`);
|
||
// printNestedStructure(nestedSemantics);
|
||
}
|
||
} catch (error) {
|
||
console.error(`Error processing ${docPath}:`, error);
|
||
}
|
||
}
|
||
|
||
// 生成 markdown 内容
|
||
let markdownContent = '# Ant Design 组件语义化描述\n\n';
|
||
markdownContent += '本文档包含了 Ant Design 组件库中所有组件的语义化描述信息。\n\n';
|
||
markdownContent += `> 总计 ${Object.keys(semanticDescriptions).length} 个组件包含语义化描述\n\n`;
|
||
markdownContent += '## 组件列表\n\n';
|
||
|
||
// 按组件名排序
|
||
const sortedComponents = Object.keys(semanticDescriptions).sort();
|
||
|
||
for (const componentName of sortedComponents) {
|
||
const semantics = semanticDescriptions[componentName];
|
||
markdownContent += `### ${componentName}\n\n`;
|
||
markdownContent += generateMarkdownStructure(semantics);
|
||
markdownContent += '\n';
|
||
}
|
||
|
||
// 生成总结(已注释掉)
|
||
// console.log('\n=== Semantic Descriptions Summary ===');
|
||
// console.log(
|
||
// `Total components with semantic descriptions: ${Object.keys(semanticDescriptions).length}`,
|
||
// );
|
||
|
||
// 将结果写入 markdown 文件
|
||
const outputPath = path.join(siteDir, 'llms-semantic.md');
|
||
await fs.writeFile(outputPath, markdownContent, 'utf-8');
|
||
console.log(`Semantic descriptions saved to: ${outputPath}`);
|
||
}
|
||
(async () => {
|
||
if (require.main === module) {
|
||
await generateSemanticDesc();
|
||
}
|
||
})().catch((e) => {
|
||
console.error(e);
|
||
process.exit(1);
|
||
});
|