Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions packages/tdesign-react-aigc/site/plugins/generate-llms/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { existsSync, promises, readFileSync } from 'fs';
import path from 'path';

import generateLlmsDocs, { createComponentDocParser } from '@tdesign/common-docs/plugins/generate-llms';

/**
* 读取组件目录下的 demo 源码:_example/<demoName>.tsx,回退 <demoName>/index.tsx。
* 输出 tsx 代码块。
*/
function readReactDemo(componentDir, demoName) {
const candidates = [
path.join(componentDir, '_example', `${demoName}.tsx`),
path.join(componentDir, '_example', demoName, 'index.tsx'),
];
for (const candidate of candidates) {
try {
const content = readFileSync(candidate, 'utf-8');
if (content.trim()) return `\`\`\`tsx\n${content.trim()}\n\`\`\``;
} catch {
// 继续尝试下一个候选路径
}
}
return '';
}

/**
* 判断是否为 demo 占位符:匹配 _example/<demoName>.tsx 或 _example/<demoName>/index.tsx。
* 本仓库 demo 为扁平 .tsx 文件(非目录),默认目录判断不命中,需自定义以正确替换 {{ demo }}。
*/
function isDemoSlot(componentDir, demoName) {
const candidates = [
path.join(componentDir, '_example', `${demoName}.tsx`),
path.join(componentDir, '_example', demoName, 'index.tsx'),
];
return candidates.some((candidate) => existsSync(candidate));
}

/**
* vite 插件:chat 站点构建时,基于 CHAT_COMPONENT_MAP 生成组件的 LLM Markdown 文档。
* 核心逻辑为纯 JS 方法 generateLlmsDocs(来自 common 的 docs/plugins/generate-llms),
* 此处仅负责 vite 构建钩子分发,并按 React AIGC 仓库约定注入组件文档与 demo 源码读取器。
*/
export default function generateChatLlms() {
let config;
return {
name: 'generate-llms',
configResolved(resolvedConfig) {
config = resolvedConfig;
},
async closeBundle(error) {
if (error) return;
if (!config.env.PROD && config.env.MODE !== 'preview') return;

// 基于 config.root 推导路径,避免依赖 __dirname 多层回溯
const siteRoot = config.root;
const componentsRoot = path.resolve(siteRoot, '../../pro-components/chat');
// 产物输出目录:从 config.build.outDir 推导,避免硬编码 dist
const outputDir = config.build.outDir || path.join(siteRoot, 'dist');

// 组件文档读取器:React AIGC 组件文档为全量内容(用法 + API),直接读取组件目录 <slug>.md;
// 不回退 common 子仓文档,避免生成 React AIGC 中不存在的组件(如 chat-content)
const readComponentDoc = async (componentDir, slug) => {
try {
return await promises.readFile(path.join(componentDir, `${slug}.md`), 'utf-8');
} catch {
return null;
}
};

// 通用文档解析管道:读取 frontmatter -> 替换 demo -> 清理正文
const parseComponentDoc = createComponentDocParser({
readComponentDoc,
readDemoCode: readReactDemo,
isDemoSlot,
transformers: [],
});

await generateLlmsDocs({
componentsRoot,
outputDir,
platform: 'chat',
parseComponentDoc,
splineLabels: { aigc: 'AI' },
siteTitle: 'TDesign React AIGC',
siteDescription: 'TDesign React AIGC 聊天组件库的 LLM 友好文档索引。',
siteBaseUrl: config.base,
});
},
};
}
3 changes: 2 additions & 1 deletion packages/tdesign-react-aigc/site/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import react from '@vitejs/plugin-react';
import pkg from '../package.json';
import tdocPlugin from './plugin-tdoc';
import changelog2Json from './plugins/changelog-to-json';
import generateLlms from './plugins/generate-llms';

// eslint-disable-next-line no-underscore-dangle
const __dirname = dirname(fileURLToPath(import.meta.url));
Expand Down Expand Up @@ -63,5 +64,5 @@ export default ({ mode }) =>
test: {
environment: 'jsdom',
},
plugins: [react(), tdocPlugin(), changelog2Json(), disableTreeShakingPlugin(['style/'])],
plugins: [react(), tdocPlugin(), changelog2Json(), generateLlms(), disableTreeShakingPlugin(['style/'])],
});
95 changes: 95 additions & 0 deletions packages/tdesign-react/site/plugins/generate-llms/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { existsSync, promises, readFileSync } from 'fs';
import path from 'path';

import generateLlmsDocs, { createComponentDocParser } from '@tdesign/common-docs/plugins/generate-llms';

/**
* 读取组件目录下的 demo 源码:_example/<demoName>.tsx,回退 <demoName>/index.tsx。
* 输出 tsx 代码块。
*/
function readReactDemo(componentDir, demoName) {
const candidates = [
path.join(componentDir, '_example', `${demoName}.tsx`),
path.join(componentDir, '_example', demoName, 'index.tsx'),
];
for (const candidate of candidates) {
try {
const content = readFileSync(candidate, 'utf-8');
if (content.trim()) return `\`\`\`tsx\n${content.trim()}\n\`\`\``;
} catch {
// 继续尝试下一个候选路径
}
}
return '';
}

/**
* 判断是否为 demo 占位符:匹配 _example/<demoName>.tsx 或 _example/<demoName>/index.tsx。
* 本仓库 demo 为扁平 .tsx 文件(非目录),默认目录判断不命中,需自定义以正确替换 {{ demo }}。
*/
function isDemoSlot(componentDir, demoName) {
const candidates = [
path.join(componentDir, '_example', `${demoName}.tsx`),
path.join(componentDir, '_example', demoName, 'index.tsx'),
];
return candidates.some((candidate) => existsSync(candidate));
}

/**
* vite 插件:站点构建时,基于组件清单映射生成组件的 LLM Markdown 文档。
* 核心逻辑为纯 JS 方法 generateLlmsDocs(来自 common 的 docs/plugins/generate-llms),
* 此处仅负责 vite 构建钩子分发,并按 React 仓库约定注入组件文档与 demo 源码读取器。
*/
export default function generateLlms() {
let config;
return {
name: 'generate-llms',
configResolved(resolvedConfig) {
config = resolvedConfig;
},
async closeBundle(error) {
if (error) return;
if (!config.env.PROD && config.env.MODE !== 'preview') return;

// 基于 config.root 推导路径,避免依赖 __dirname 多层回溯
const siteRoot = config.root;
const componentsRoot = path.resolve(siteRoot, '../../components');
// common 子仓文档根目录(组件文档为 <slug>.md,如 packages/common/docs/web/api/button.md)
const docsRoot = path.resolve(siteRoot, '../../common/docs');
// 产物输出目录:从 config.build.outDir 推导,避免硬编码 dist
const outputDir = config.build.outDir || path.join(siteRoot, 'dist');

// 组件文档读取器:common 子仓扁平目录 <slug>.md(用法示例),
// 并追加组件目录 <slug>.md 的 API 部分(站点构建时由 :: BASE_DOC :: 注入,此处手动拼接)
const readComponentDoc = async (componentDir, slug) => {
const docPath = path.join(docsRoot, 'web/api', `${slug}.md`);
try {
const raw = await promises.readFile(docPath, 'utf-8');
const apiDoc = await promises.readFile(path.join(componentDir, `${slug}.md`), 'utf-8').catch(() => '');
const apiBody = apiDoc.replace(/^[^\S\n]*::\s*BASE_DOC\s*::[^\S\n]*\n?/m, '').trim();
return apiBody ? `${raw}\n\n${apiBody}\n` : raw;
} catch {
return null;
}
};

// 通用文档解析管道:读取 frontmatter -> 替换 demo -> 清理正文
const parseComponentDoc = createComponentDocParser({
readComponentDoc,
readDemoCode: readReactDemo,
isDemoSlot,
transformers: [],
});

await generateLlmsDocs({
componentsRoot,
outputDir,
platform: 'web',
parseComponentDoc,
siteTitle: 'TDesign React',
siteDescription: 'TDesign React 组件库的 LLM 友好文档索引。',
siteBaseUrl: config.base,
});
},
};
}
18 changes: 13 additions & 5 deletions packages/tdesign-react/site/vite.config.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import react from '@vitejs/plugin-react';
import path, { dirname } from 'path';
import { fileURLToPath } from 'url';

import { defineConfig } from 'vite';
import { VitePWA } from 'vite-plugin-pwa';
import pkg from '../package.json';

import pwaConfig from './pwaConfig';
import react from '@vitejs/plugin-react';

import pkg from '../package.json';
import changelog2Json from './plugins/changelog-to-json';
import generateLlms from './plugins/generate-llms';
import tdocPlugin from './plugins/plugin-tdoc';
import pwaConfig from './pwaConfig';

// eslint-disable-next-line no-underscore-dangle
const __dirname = dirname(fileURLToPath(import.meta.url));
Expand Down Expand Up @@ -62,5 +63,12 @@ export default ({ mode }) =>
test: {
environment: 'jsdom',
},
plugins: [react(), tdocPlugin(), changelog2Json(), VitePWA(pwaConfig), disableTreeShakingPlugin(['style/'])],
plugins: [
react(),
tdocPlugin(),
changelog2Json(),
generateLlms(),
VitePWA(pwaConfig),
disableTreeShakingPlugin(['style/']),
],
});
Loading