diff --git a/.gitignore b/.gitignore index 7c87415936..81c8412857 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,9 @@ node_modules /dist /dist-demo /dist-site +# meta 派生产物:components.json 由 generate:meta 产出,各平台 llms 由 generate:llms 产出 +# (build:site / build:taro:site 前自动生成),整个目录均为构建产物,不落库 +/meta/ /release /libs /jd/upload.js diff --git a/package.json b/package.json index 08213ba72b..8cd5d6ef13 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,9 @@ "build:demo:jmapp": "npm run checked && VITE_APP_PROJECT_ID=jmapp vite build --config vite.config.demo.ts", "prebuild:taro:demo": "SKIPDD=1 pnpm run update:taro:entry", "build:taro:demo": "npm run checked:taro && npm run generate:file:taro:pages && pnpm --dir ./packages/nutui-taro-demo build:h5", + "prebuild:site": "npm run generate:meta && npm run generate:llms", "build:site": "vite build --config vite.config.site.mts", + "prebuild:taro:site": "npm run generate:meta && npm run generate:llms", "build:taro:site": "vite build --config vite.config.site.taro.mts && node ./scripts/rename.js", "build:jdtaro:demo": "npm run checked:taro && npm run generate:file:taro:pages && JD=1 pnpm --dir ./packages/nutui-taro-demo build:h5", "build:taro:demo:jmapp": "npm run checked:taro && VITE_APP_PROJECT_ID=jmapp npm run generate:file:taro:pages && pnpm --dir ./packages/nutui-taro-demo build:jmapp h5", @@ -86,6 +88,8 @@ "generate:file:taro": "node scripts/taro/generate-nutui-taro.js", "generate:file:taro:pages": "node scripts/taro/generate-taro-pages.js", "generate:props_json": "PROPS_JSON=true node scripts/build-comments-to-dts.mjs", + "generate:meta": "node scripts/build-meta.mjs", + "generate:llms": "node scripts/build-llms.mjs", "lint": "eslint ./src/packages/*/", "lint:fix": "eslint --fix ./src/packages", "prepare": "husky && npm run generate:file && npm run generate:file:taro && npm run generate:file:taro:pages", diff --git a/scripts/build-llms.mjs b/scripts/build-llms.mjs new file mode 100644 index 0000000000..6004e8a452 --- /dev/null +++ b/scripts/build-llms.mjs @@ -0,0 +1,173 @@ +// 读 meta/components.json,为每个文档站平台(H5 / Taro)生成面向 LLM 的文档入口产物: +// - llms.txt 导航索引(仿 llmstxt.org 标准) +// - llms-full-cn.txt 全部中文 doc 拼接,可整体注入上下文 +// - llms-full.txt 全部英文 doc 拼接(仅 H5,Taro 端无英文文档) +// 核心生成逻辑按平台参数化并导出为纯函数,供 vite.config.site(.taro) 的插件复用,避免多处实现。 +// DO NOT manual edit the outputs. Run: npm run generate:llms +import fs from 'fs' +import path from 'path' +import { fileURLToPath, pathToFileURL } from 'url' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) +const ROOT = path.resolve(__dirname, '..') +const META_PATH = path.join(ROOT, 'meta/components.json') +const OUTPUT_DIR = path.join(ROOT, 'meta') + +export const SITE_ORIGIN = 'https://nutui.jd.com' + +const SUMMARY = + 'NutUI React 是京东风格的轻量级移动端 React 组件库,支持一套代码生成 H5 和小程序(Taro 多端)。' +const SUMMARY_EN = + 'NutUI React is a lightweight, JD-style mobile React UI library that generates both H5 and mini-program (Taro multi-platform) apps from a single codebase.' + +// 各文档站平台的差异集中于此:站点 base、文档来源(meta.docs 的哪个 key)、标题落款、是否有英文文档。 +// 单组件 .md 路由由 site 插件产出到 /components/.md。 +export const PLATFORMS = { + h5: { + base: '/h5/react/4x', + // 中/英文正文各取哪个 docs key。H5 端中文用 doc.md、英文用 doc.en-US.md。 + langDocKey: { cn: 'h5', en: 'enUS' }, + titleSuffix: '', + pkgName: '@nutui/nutui-react', + }, + taro: { + base: '/taro/react/4x', + // Taro 端中文用 doc.taro.md;暂无 Taro 英文文档,故不产出 llms-full.txt。 + langDocKey: { cn: 'taro' }, + titleSuffix: '(Taro 多端)', + pkgName: '@nutui/nutui-react-taro', + }, +} + +export function loadMeta() { + return JSON.parse(fs.readFileSync(META_PATH, 'utf-8')) +} + +const siteUrl = (platform) => `${SITE_ORIGIN}${PLATFORMS[platform].base}` + +// 仅纳入在该平台有中文文档的组件(= show:true 的展示组件),按 categories 的 nav 顺序输出。 +function docComponents(meta, cnKey) { + const list = [] + for (const cat of meta.categories) { + const comps = cat.components + .map((id) => meta.components[id]) + .filter((c) => c && c.docs && c.docs[cnKey]) + if (comps.length) list.push({ category: cat, comps }) + } + return list +} + +export function generateLlmsTxt(meta, platform = 'h5') { + const conf = PLATFORMS[platform] + const cnKey = conf.langDocKey.cn + const url = siteUrl(platform) + const groups = docComponents(meta, cnKey) + const lines = [] + lines.push(`# NutUI React - 京东风格轻量级移动端 React 组件库${conf.titleSuffix}`) + lines.push('') + lines.push( + `> ${SUMMARY}当前为 ${conf.pkgName}(\`${platform}\` 端),版本 ${meta.libVersion},共 ${meta.componentCount} 个组件。` + ) + lines.push('') + lines.push( + `本文件遵循 llmstxt.org 标准,为 AI 编程助手提供权威、离线可查的组件文档入口。每个组件的 \`.md\` 链接返回其纯文档原文(无站点 HTML 噪音),可按需精确拉取。` + ) + lines.push('') + lines.push('## Navigation') + lines.push('') + lines.push(`- [全部组件文档(中文,单文件)](${url}/llms-full-cn.txt)`) + if (conf.langDocKey.en) { + lines.push(`- [Full Documentation (English, single file)](${url}/llms-full.txt)`) + } + lines.push('') + + for (const { category, comps } of groups) { + lines.push(`## ${category.name}${category.enName ? ` (${category.enName})` : ''}`) + lines.push('') + for (const c of comps) { + const label = c.cName ? `${c.name} ${c.cName}` : c.name + lines.push(`- [${label}](${url}/components/${c.id}.md)`) + } + lines.push('') + } + return `${lines.join('\n').trimEnd()}\n` +} + +// 拼接该平台全部组件文档为单文件。lang: 'cn' | 'en'(en 仅 H5 有)。 +export function generateLlmsFull(meta, platform = 'h5', lang = 'cn') { + const conf = PLATFORMS[platform] + const docKey = conf.langDocKey[lang] + const title = + lang === 'en' + ? '# NutUI React - Full Component Documentation' + : `# NutUI React - 全部组件文档${conf.titleSuffix}` + const summary = lang === 'en' ? SUMMARY_EN : SUMMARY + const parts = [title, '', `> ${summary}`, ''] + for (const cat of meta.categories) { + for (const id of cat.components) { + const c = meta.components[id] + const rel = c && c.docs ? c.docs[docKey] : null + if (!rel) continue + const abs = path.join(ROOT, rel) + if (!fs.existsSync(abs)) continue + parts.push('---', '') + parts.push(fs.readFileSync(abs, 'utf-8').trim()) + parts.push('') + } + } + return `${parts.join('\n').trimEnd()}\n` +} + +// 汇总某平台要产出的全部 llms 文件:{ 相对站点根的路径 -> 文本内容 }。 +// 含 llms.txt / llms-full*.txt 与每组件的 components/.md(取该平台对应端文档)。 +// 供 build-llms 的 main 与 vite 插件共用,保证两处产物完全一致。 +export function collectLlmsFiles(meta, platform = 'h5') { + const conf = PLATFORMS[platform] + const cnKey = conf.langDocKey.cn + const files = { + 'llms.txt': generateLlmsTxt(meta, platform), + 'llms-full-cn.txt': generateLlmsFull(meta, platform, 'cn'), + } + if (conf.langDocKey.en) { + files['llms-full.txt'] = generateLlmsFull(meta, platform, 'en') + } + for (const c of Object.values(meta.components)) { + const rel = c.docs && c.docs[cnKey] + if (!rel) continue + const abs = path.join(ROOT, rel) + if (!fs.existsSync(abs)) continue + files[`components/${c.id}.md`] = fs.readFileSync(abs, 'utf-8') + } + return files +} + +function main() { + console.log('🚀 building llms.txt ...') + const meta = loadMeta() + for (const platform of Object.keys(PLATFORMS)) { + const outDir = path.join(OUTPUT_DIR, platform) + fs.mkdirSync(outDir, { recursive: true }) + // 磁盘产物仅保留导航与全量文件;单组件 md 由站点构建时按需产出,不落 meta。 + const files = { + 'llms.txt': generateLlmsTxt(meta, platform), + 'llms-full-cn.txt': generateLlmsFull(meta, platform, 'cn'), + } + if (PLATFORMS[platform].langDocKey.en) { + files['llms-full.txt'] = generateLlmsFull(meta, platform, 'en') + } + for (const [name, content] of Object.entries(files)) { + const out = path.join(outDir, name) + fs.writeFileSync(out, content) + const kb = (Buffer.byteLength(content) / 1024).toFixed(0) + console.log(`✅ ${path.relative(ROOT, out)} (${kb}KB)`) + } + const cnKey = PLATFORMS[platform].langDocKey.cn + const docCount = docComponents(meta, cnKey).reduce((n, g) => n + g.comps.length, 0) + console.log(` [${platform}] 索引组件数: ${docCount}`) + } +} + +// 仅作为脚本直接运行时执行 main,被 import 时不执行。 +if (import.meta.url === pathToFileURL(process.argv[1]).href) { + main() +} diff --git a/scripts/build-meta.mjs b/scripts/build-meta.mjs new file mode 100644 index 0000000000..72cf0b0588 --- /dev/null +++ b/scripts/build-meta.mjs @@ -0,0 +1,300 @@ +// 聚合 config.json + properties.json + demos + variables.scss,输出 meta/components.json。 +// 面向 AI Coding 的「单一事实源」中间产物,供后续 llms.txt / CLI / MCP 共同消费。 +// DO NOT manual edit the output (meta/components.json). Run: npm run generate:meta +import fs from 'fs' +import path from 'path' +import { fileURLToPath } from 'url' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) +const ROOT = path.resolve(__dirname, '..') + +const CONFIG_PATH = path.join(ROOT, 'src/config.json') +const PROPERTIES_PATH = path.join(ROOT, 'scripts/properties.json') +const VARIABLES_PATH = path.join(ROOT, 'src/styles/variables.scss') +const PACKAGES_DIR = path.join(ROOT, 'src/packages') +const SPEC_DIR = path.join(ROOT, 'src/types/spec') +const PKG_JSON_PATH = path.join(ROOT, 'package.json') +const OUTPUT_PATH = path.join(ROOT, 'meta/components.json') + +const SCHEMA_VERSION = '1.0.0' + +// properties.json 的「组件名」经三级归一后仍无法匹配 config 的特例,手工兜底。 +// 注:SideNavBar 系列在 src/packages 有目录但未注册进 config.nav,故不设别名, +// 让其如实落入 orphan-api 警告(提示「有文档但组件未注册」)。 +const COMPONENT_ALIAS = { + IconFont: 'icon', +} + +const warnings = [] +const warn = (msg) => warnings.push(msg) + +// 把 properties 的中文位置式列映射为语义字段。语义严格只对 props 表成立, +// 枚举/结构表(kind !== 'props')的四列语义不同,消费方应按 kind 重新解释。 +const mapRow = (rec) => ({ + prop: (rec['第一列'] || '').trim(), + desc: (rec['第二列'] || '').trim(), + type: (rec['第三列'] || '').trim(), + default: (rec['第四列'] || '').trim(), +}) + +// 归一表格名,仅用于合并大小写差异(Props vs props),不合并语义变体(Props(direction=...))。 +const normalizeTableName = (name) => (name || '').trim().toLowerCase() + +const classifyKind = (name) => { + const t = normalizeTableName(name) + if (t === 'ref') return 'ref' + if (t === 'methods' || t === 'forminstance') return 'methods' + if (t.startsWith('props')) return 'props' + return 'other' +} + +// config 的组件 name(PascalCase)→ 目录名(既有约定:name.toLowerCase())。 +const toDirId = (name) => String(name).toLowerCase() + +// 把 properties 的「组件名」归属到某个 config 组件 id。三级优先级: +// 1) 去点拼接小写:子组件多在 config 有独立条目(Checkbox.Group→checkboxgroup) +// 2) 首段小写:无独立条目时归到父组件(Tabs.Tabpane→tabs) +// 3) 别名表兜底(IconFont→icon、SideNavBar 系列→sidenavbar) +const resolveApiId = (rawComponentName, validIds) => { + const raw = String(rawComponentName) + if (COMPONENT_ALIAS[raw]) return COMPONENT_ALIAS[raw] + const joined = raw.replace(/\./g, '').toLowerCase() + if (validIds.has(joined)) return joined + const firstSeg = raw.split('.')[0].toLowerCase() + if (validIds.has(firstSeg)) return firstSeg + return joined // 落 orphan,保留去点形式便于诊断 +} + +// ---- A. properties.json → bucket[id] = { normKey -> table } ---- +function buildApiBucket(properties, validIds) { + const bucket = {} + const orphan = new Set() + + for (const rec of properties) { + const raw = rec['组件名'] + const id = resolveApiId(raw, validIds) + if (!validIds.has(id)) { + orphan.add(`${raw} (id=${id})`) + continue + } + const tableNameRaw = rec['表格名'] || 'Props' + const normKey = normalizeTableName(tableNameRaw) + // 仅当表来自父组件视角(raw 含点且归到了父组件 id)时才标记子组件来源; + // 归到独立子组件条目时它就是该组件本身,subComponent 记 null。 + const hasDot = String(raw).includes('.') + const joinedId = String(raw).replace(/\./g, '').toLowerCase() + const subComponent = + hasDot && id !== joinedId ? String(raw).split('.').slice(1).join('.') : null + // 合并键含来源组件:同一 id 下不同原始组件的同名表(如 Icon 与 IconFont 的 Props) + // 应保持为独立表格,只有完全同源的表才合并大小写差异。 + const groupKey = `${raw}@@${normKey}` + + if (!bucket[id]) bucket[id] = {} + if (!bucket[id][groupKey]) { + bucket[id][groupKey] = { + name: tableNameRaw, + kind: classifyKind(tableNameRaw), + sourceComponent: raw, + subComponent, + rows: [], + } + } else if (bucket[id][groupKey].name !== tableNameRaw) { + warn( + `[table-case] "${id}" 合并大小写差异表格:「${bucket[id][groupKey].name}」与「${tableNameRaw}」` + ) + } + bucket[id][groupKey].rows.push(mapRow(rec)) + } + + if (orphan.size) { + warn( + `[orphan-api] ${orphan.size} 个 properties 组件名无法匹配 config,未注入:${[...orphan].join(',')}` + ) + } + return bucket +} + +// bucket[id] → api.tables 数组 + Props 行内质量校验 +function bucketToApi(id, tablesMap) { + if (!tablesMap) return { tables: [] } + const tables = Object.values(tablesMap) + for (const t of tables) { + if (t.kind !== 'props') continue + const seen = new Set() + for (const row of t.rows) { + if (!row.prop || !row.type) { + warn(`[props-field] "${id}" 表「${t.name}」存在缺列行:prop="${row.prop}" type="${row.type}"`) + } + if (row.prop) { + if (seen.has(row.prop)) warn(`[dup-prop] "${id}" 表「${t.name}」prop 重复:${row.prop}`) + seen.add(row.prop) + } + } + } + return { tables } +} + +// ---- B. variables.scss → tokens(按最长组件目录前缀归属,否则 global)---- +function parseTokens(validIdsSorted) { + const content = fs.readFileSync(VARIABLES_PATH, 'utf-8') + // 仅匹配「$scssVar: var(--nutui-xxx[, default]) !default;」的定义行(单行)。 + const re = /^\s*\$([\w-]+):\s*var\((--nutui-[\w-]+)\s*(?:,\s*([^;]*?))?\)\s*!default/gm + const perComponent = {} + const globalTokens = [] + let m + while ((m = re.exec(content)) !== null) { + const scssVar = `$${m[1]}` + const cssVar = m[2] + const def = m[3] != null ? m[3].trim() : null + const token = { cssVar, scssVar, default: def } + const rest = cssVar.slice('--nutui-'.length) + // 最长匹配:validIdsSorted 已按长度降序 + const owner = validIdsSorted.find( + (id) => rest === id || rest.startsWith(`${id}-`) + ) + if (owner) { + ;(perComponent[owner] ||= []).push(token) + } else { + globalTokens.push(token) + } + } + return { perComponent, globalTokens } +} + +// ---- demos 扫描 ---- +function scanDemos(id, sub) { + const dir = path.join(PACKAGES_DIR, id, 'demos', sub) + if (!fs.existsSync(dir)) return [] + return fs + .readdirSync(dir) + .map((f) => f.match(/^demo(\d+)\.tsx$/)) + .filter(Boolean) + .sort((a, b) => Number(a[1]) - Number(b[1])) + .map((mt) => `src/packages/${id}/demos/${sub}/${mt[0]}`) +} + +// ---- docs 存在性 ---- +function resolveDocs(id, showFlag) { + const map = { + h5: 'doc.md', + enUS: 'doc.en-US.md', + zhTW: 'doc.zh-TW.md', + taro: 'doc.taro.md', + } + const docs = {} + for (const [key, file] of Object.entries(map)) { + const rel = `src/packages/${id}/${file}` + docs[key] = fs.existsSync(path.join(ROOT, rel)) ? rel : null + } + if (showFlag && !docs.h5) warn(`[doc-missing] "${id}" show:true 但缺 doc.md`) + return docs +} + +// ---- spec 引用(可选,仅存在时输出)---- +function resolveSpec(id) { + const base = path.join(SPEC_DIR, id) + if (!fs.existsSync(base)) return null + const spec = {} + for (const key of ['base', 'h5', 'taro']) { + const rel = `src/types/spec/${id}/${key}.ts` + if (fs.existsSync(path.join(ROOT, rel))) spec[key] = rel + } + return Object.keys(spec).length ? spec : null +} + +function main() { + console.log('🚀 building meta/components.json ...') + const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8')) + const properties = JSON.parse(fs.readFileSync(PROPERTIES_PATH, 'utf-8')) + const pkg = JSON.parse(fs.readFileSync(PKG_JSON_PATH, 'utf-8')) + + // config.nav → 主索引 + const validIds = new Set() + const navPairs = [] // { cat, pkg, id } + for (const cat of config.nav) { + for (const p of cat.packages) { + const id = toDirId(p.name) + validIds.add(id) + navPairs.push({ cat, p, id }) + } + } + const validIdsSorted = [...validIds].sort((a, b) => b.length - a.length) + + const bucket = buildApiBucket(properties, validIds) + const { perComponent: tokensByComp, globalTokens } = parseTokens(validIdsSorted) + + const components = {} + const categories = [] + const noApiIds = [] + + for (const cat of config.nav) { + const catRef = { name: cat.name, enName: cat.enName, components: [] } + for (const p of cat.packages) { + const id = toDirId(p.name) + const api = bucketToApi(id, bucket[id]) + if (!api.tables.length) noApiIds.push(id) + + const entry = { + id, + name: p.name, + cName: p.cName, + version: p.version, + category: { name: cat.name, enName: cat.enName }, + taro: !!p.taro, + v15: !!p.v15, + v16: !!p.v16, + } + // 可选原样字段 + for (const k of ['author', 'type', 'desc', 'sort', 'show', 'rn', 'exclude', 'jdtaro']) { + if (p[k] !== undefined) entry[k] = p[k] + } + entry.docs = resolveDocs(id, p.show) + entry.demos = { h5: scanDemos(id, 'h5'), taro: scanDemos(id, 'taro') } + const spec = resolveSpec(id) + if (spec) entry.spec = spec + entry.api = api + entry.tokens = tokensByComp[id] || [] + + if (p.show && !entry.demos.h5.length && !entry.demos.taro.length) { + warn(`[demo-empty] "${id}" show:true 但无 demo`) + } + if (!cat.enName) warn(`[category] 分类「${cat.name}」缺 enName`) + + components[id] = entry + catRef.components.push(id) + } + categories.push(catRef) + } + + const componentCount = Object.keys(components).length + const apiComponentCount = componentCount - noApiIds.length + + const output = { + schemaVersion: SCHEMA_VERSION, + libVersion: pkg.version, + componentCount, + apiComponentCount, + categories, + globalTokens, + components, + } + + fs.mkdirSync(path.dirname(OUTPUT_PATH), { recursive: true }) + fs.writeFileSync(OUTPUT_PATH, `${JSON.stringify(output, null, 2)}\n`) + + // ---- 对账 & 汇总 ---- + console.log(`✅ 写入 ${path.relative(ROOT, OUTPUT_PATH)}`) + console.log(` 组件总数: ${componentCount},有 API 表: ${apiComponentCount}`) + console.log(` 全局 token: ${globalTokens.length}`) + if (noApiIds.length) { + console.log(` 无 API 表的组件 (${noApiIds.length}): ${noApiIds.join(', ')}`) + } + if (warnings.length) { + console.log(`\n⚠️ ${warnings.length} 条 WARNING:`) + for (const w of warnings) console.log(` - ${w}`) + } else { + console.log('\n✨ 无 WARNING') + } +} + +main() diff --git a/scripts/site/vite-plugin-llms.mjs b/scripts/site/vite-plugin-llms.mjs new file mode 100644 index 0000000000..4dac349b9c --- /dev/null +++ b/scripts/site/vite-plugin-llms.mjs @@ -0,0 +1,54 @@ +// Vite 插件(用于文档站 vite.config.site.mts 与 vite.config.site.taro.mts): +// - build 时:把每个组件对应端的 doc 原文与 llms.txt/llms-full*.txt 通过 emitFile +// 输出为静态文件,最终落到 dist-site/<...>/components/.md 与 dist-site/<...>/*.txt +// - dev 时:configureServer 中间件按同样路径实时返回,便于本地联调 +// 平台差异(base、取哪一端文档)由 build-llms.mjs 的 PLATFORMS 决定,此处仅按 platform 透传。 +import { + loadMeta, + collectLlmsFiles, + PLATFORMS, +} from '../build-llms.mjs' + +const contentType = (p) => + p.endsWith('.md') + ? 'text/markdown; charset=utf-8' + : 'text/plain; charset=utf-8' + +// platform: 'h5' | 'taro',决定站点 base 与单组件 md 取哪一端文档。 +export default function llmsPlugin({ platform = 'h5' } = {}) { + const base = PLATFORMS[platform].base.replace(/\/$/, '') + return { + name: 'nutui-llms', + + // build 阶段:把虚拟文件写入产物目录(相对 outDir,即 dist-site/)。 + generateBundle() { + const files = collectLlmsFiles(loadMeta(), platform) + for (const [fileName, source] of Object.entries(files)) { + this.emitFile({ type: 'asset', fileName, source }) + } + console.log( + `[nutui-llms:${platform}] emitted ${Object.keys(files).length} files (llms*.txt + components/*.md)` + ) + }, + + // dev 阶段:中间件按 /llms.txt、/components/.md 实时返回。 + configureServer(server) { + server.middlewares.use((req, res, next) => { + const url = (req.url || '').split('?')[0] + const rel = url.startsWith(base) ? url.slice(base.length) : url + const key = rel.replace(/^\//, '') + const isTarget = + key === 'llms.txt' || + key === 'llms-full-cn.txt' || + key === 'llms-full.txt' || + (key.startsWith('components/') && key.endsWith('.md')) + if (!isTarget) return next() + + const files = collectLlmsFiles(loadMeta(), platform) + if (!(key in files)) return next() + res.setHeader('Content-Type', contentType(key)) + res.end(files[key]) + }) + }, + } +} diff --git a/vite.config.site.mts b/vite.config.site.mts index b0a8ace5b4..67c9afd415 100644 --- a/vite.config.site.mts +++ b/vite.config.site.mts @@ -6,6 +6,8 @@ import { join, resolve } from 'path' import atImport from 'postcss-import' import rehypeHighlight from 'rehype-highlight' import config from './package.json' +// @ts-ignore +import llmsPlugin from './scripts/site/vite-plugin-llms.mjs' const projectID = process.env.VITE_APP_PROJECT_ID || '' @@ -64,6 +66,7 @@ export default defineConfig(async (): Promise => { }), }, reactRefresh(), + llmsPlugin({ platform: 'h5' }), ], build: { outDir: './dist-site/h5', diff --git a/vite.config.site.taro.mts b/vite.config.site.taro.mts index 022b4c6d17..5d4fd3e9fd 100644 --- a/vite.config.site.taro.mts +++ b/vite.config.site.taro.mts @@ -6,6 +6,8 @@ import { join, resolve } from 'path' import atImport from 'postcss-import' import rehypeHighlight from 'rehype-highlight' import config from './package.json' +// @ts-ignore +import llmsPlugin from './scripts/site/vite-plugin-llms.mjs' const projectID = process.env.VITE_APP_PROJECT_ID || '' @@ -64,6 +66,7 @@ export default defineConfig(async (): Promise => { }), }, reactRefresh(), + llmsPlugin({ platform: 'taro' }), ], build: { outDir: './dist-site/taro',