diff --git a/packages/nutui-react-cli-core/package.json b/packages/nutui-react-cli-core/package.json index 3abc909086..0a02ae41e5 100644 --- a/packages/nutui-react-cli-core/package.json +++ b/packages/nutui-react-cli-core/package.json @@ -8,15 +8,26 @@ "author": "jdcfe", "exports": { ".": "./src/index.ts", - "./scripts/prepare-data.mjs": "./scripts/prepare-data.mjs" + "./scripts/prepare-data.mjs": "./scripts/prepare-data.mjs", + "./scripts/sync.mjs": "./scripts/sync.mjs", + "./scripts/build-meta.mjs": "./scripts/build-meta.mjs", + "./scripts/create-properties.mjs": "./scripts/create-properties.mjs" + }, + "scripts": { + "test": "vitest run", + "test:watch": "vitest" }, "dependencies": { "@modelcontextprotocol/sdk": "^1.30.0", + "semver": "^7.8.0", "yargs": "^17.7.3" }, "devDependencies": { "@types/node": "^22.20.1", + "@types/semver": "^7.7.1", "@types/yargs": "^17.0.35", - "typescript": "^5.6.2" + "markdown-it": "^14.2.0", + "typescript": "^5.6.2", + "vitest": "^3.2.7" } } diff --git a/packages/nutui-react-cli-core/scripts/build-meta.mjs b/packages/nutui-react-cli-core/scripts/build-meta.mjs new file mode 100644 index 0000000000..0c1bb59595 --- /dev/null +++ b/packages/nutui-react-cli-core/scripts/build-meta.mjs @@ -0,0 +1,380 @@ +// 聚合 config.json + properties.json + demos + variables.scss,输出 components.json(meta)。 +// 面向 AI Coding 的「单一事实源」中间产物,供后续 llms.txt / CLI / MCP 共同消费。 +// +// 本文件是仓库根 scripts/build-meta.mjs 的「可参数化」版本:所有输入路径与 CSS 变量 +// 前缀都改为入参,从而能对任意 git tag 的 worktree(3.x / 4.x)跑同一套抽取逻辑。 +// 详见 packages/nutui-react-cli-core/scripts/sync.mjs 的多版本快照流程。 +// +// DO NOT manual edit the output. Run via prepare-data / sync. +import fs from 'node:fs' +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +const SCHEMA_VERSION = '1.0.0' + +// properties.json 的「组件名」经三级归一后仍无法匹配 config 的特例,手工兜底。 +// 注:SideNavBar 系列在 src/packages 有目录但未注册进 config.nav,故不设别名, +// 让其如实落入 orphan-api 警告(提示「有文档但组件未注册」)。 +const COMPONENT_ALIAS = { + IconFont: 'icon', +} + +// 把 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) +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, warn, orphanLabel = 'orphan-api') { + 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( + `[${orphanLabel}] ${orphan.size} 个 properties 组件名无法匹配 config,未注入:${[...orphan].join(',')}` + ) + } + return bucket +} + +// bucket[id] → api.tables 数组 + Props 行内质量校验 +function bucketToApi(id, tablesMap, warn) { + 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)---- +// cssVarPrefix 参数化:v3/v4 的组件 token 体系均为 --nutui-(实测 3.x 亦然), +// 仅保留入参以便未来某个 major 若切换前缀时无需改代码。 +function parseTokens(variablesPath, validIdsSorted, cssVarPrefix) { + const content = fs.readFileSync(variablesPath, 'utf-8') + const escPrefix = cssVarPrefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + // 仅匹配「$scssVar: var(--xxx[, default]) !default;」的定义行(单行)。 + const re = new RegExp( + `^\\s*\\$([\\w-]+):\\s*var\\((${escPrefix}[\\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(cssVarPrefix.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(packagesDir, id, sub) { + const dir = path.join(packagesDir, 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(repoRoot, id, showFlag, warn) { + 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(repoRoot, rel)) ? rel : null + } + if (showFlag && !docs.h5) warn(`[doc-missing] "${id}" show:true 但缺 doc.md`) + return docs +} + +// ---- spec 引用(可选,仅存在时输出)---- +function resolveSpec(repoRoot, specDir, id) { + if (!specDir) return null + const base = path.join(specDir, 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(repoRoot, rel))) spec[key] = rel + } + return Object.keys(spec).length ? spec : null +} + +/** + * 从一个 NutUI 仓库 checkout(任意 tag 的 worktree)抽取 meta/components.json 结构。 + * 所有输入路径都可注入,因此同一套逻辑可作用于 3.x / 4.x 的不同源码树。 + * + * @param {object} opts + * @param {string} opts.repoRoot 仓库根(worktree)绝对路径 + * @param {string} opts.configPath src/config.json 绝对路径 + * @param {string} opts.propertiesPath scripts/properties.json 绝对路径 + * @param {string} [opts.propertiesTaroPath] scripts/properties-taro.json(缺失则 apiTaro 空表 + warn) + * @param {string} opts.packagesDir src/packages 绝对路径 + * @param {string} opts.variablesPath src/styles/variables.scss 绝对路径 + * @param {string} opts.pkgJsonPath package.json 绝对路径(取 libVersion) + * @param {string} [opts.specDir] src/types/spec 绝对路径(可选) + * @param {string} [opts.cssVarPrefix='--nutui-'] 组件 token 的 CSS 变量前缀 + * @param {string} opts.out 输出 components.json 的绝对路径 + * @returns {{ globalTokens: number, componentCount: number, libVersion: string, warnings: string[] }} + */ +export function buildMeta(opts) { + const { + repoRoot, + configPath, + propertiesPath, + propertiesTaroPath, + packagesDir, + variablesPath, + pkgJsonPath, + specDir, + cssVarPrefix = '--nutui-', + out, + } = opts + + const warnings = [] + const warn = (msg) => warnings.push(msg) + + const config = JSON.parse(fs.readFileSync(configPath, 'utf-8')) + const properties = JSON.parse(fs.readFileSync(propertiesPath, 'utf-8')) + // taro 端 Props(源自 doc.taro.md)。为可选产物:缺失时 apiTaro 落空表并告警,不中断。 + let propertiesTaro = null + if (propertiesTaroPath && fs.existsSync(propertiesTaroPath)) { + propertiesTaro = JSON.parse(fs.readFileSync(propertiesTaroPath, 'utf-8')) + } else { + warn( + `[props-taro-missing] 未找到 properties-taro.json,apiTaro 全部为空。` + ) + } + const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8')) + + // config.nav → 主索引 + const validIds = new Set() + for (const cat of config.nav) { + for (const p of cat.packages) { + validIds.add(toDirId(p.name)) + } + } + const validIdsSorted = [...validIds].sort((a, b) => b.length - a.length) + + const bucket = buildApiBucket(properties, validIds, warn) + const bucketTaro = propertiesTaro + ? buildApiBucket(propertiesTaro, validIds, warn, 'orphan-api-taro') + : {} + const { perComponent: tokensByComp, globalTokens } = parseTokens( + variablesPath, + validIdsSorted, + cssVarPrefix + ) + + const components = {} + const categories = [] + const noApiIds = [] + const noApiTaroIds = [] + + 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], warn) + const apiTaro = bucketToApi(id, bucketTaro[id], warn) + if (!api.tables.length) noApiIds.push(id) + if (!apiTaro.tables.length) noApiTaroIds.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(repoRoot, id, p.show, warn) + entry.demos = { + h5: scanDemos(packagesDir, id, 'h5'), + taro: scanDemos(packagesDir, id, 'taro'), + } + const spec = resolveSpec(repoRoot, specDir, id) + if (spec) entry.spec = spec + entry.api = api + entry.apiTaro = apiTaro + 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 apiTaroComponentCount = componentCount - noApiTaroIds.length + + const output = { + schemaVersion: SCHEMA_VERSION, + libVersion: pkg.version, + componentCount, + apiComponentCount, + apiTaroComponentCount, + categories, + globalTokens, + components, + } + + fs.mkdirSync(path.dirname(out), { recursive: true }) + fs.writeFileSync(out, `${JSON.stringify(output, null, 2)}\n`) + + return { + globalTokens: globalTokens.length, + componentCount, + apiComponentCount, + apiTaroComponentCount, + libVersion: pkg.version, + noApiIds, + warnings, + } +} + +// 独立运行兜底:从仓库根默认路径跑一次(等价于原 scripts/build-meta.mjs), +// 便于在 core 目录直接 `node scripts/build-meta.mjs` 调试当前 checkout。 +function main() { + const __dirname = path.dirname(fileURLToPath(import.meta.url)) + // packages/nutui-react-cli-core/scripts -> 仓库根 + const ROOT = path.resolve(__dirname, '..', '..', '..') + console.log('🚀 building meta/components.json (standalone) ...') + const res = buildMeta({ + repoRoot: ROOT, + configPath: path.join(ROOT, 'src/config.json'), + propertiesPath: path.join(ROOT, 'scripts/properties.json'), + propertiesTaroPath: path.join(ROOT, 'scripts/properties-taro.json'), + packagesDir: path.join(ROOT, 'src/packages'), + variablesPath: path.join(ROOT, 'src/styles/variables.scss'), + pkgJsonPath: path.join(ROOT, 'package.json'), + specDir: path.join(ROOT, 'src/types/spec'), + cssVarPrefix: '--nutui-', + out: path.join(ROOT, 'meta/components.json'), + }) + console.log( + `✅ 组件 ${res.componentCount},有 API 表(H5) ${res.apiComponentCount},全局 token ${res.globalTokens},libVersion ${res.libVersion}` + ) + if (res.warnings.length) { + console.log(`\n⚠️ ${res.warnings.length} 条 WARNING:`) + for (const w of res.warnings) console.log(` - ${w}`) + } else { + console.log('\n✨ 无 WARNING') + } +} + +if (process.argv[1] && fileURLToPath(import.meta.url) === path.resolve(process.argv[1])) { + main() +} diff --git a/packages/nutui-react-cli-core/scripts/create-properties.mjs b/packages/nutui-react-cli-core/scripts/create-properties.mjs new file mode 100644 index 0000000000..70bb0adb29 --- /dev/null +++ b/packages/nutui-react-cli-core/scripts/create-properties.mjs @@ -0,0 +1,126 @@ +// 扫描组件文档(doc.md / doc.taro.md),抽取 Properties / Ref 等表格,输出 properties(.json)。 +// +// 这是把仓库内 scripts/create-properties.js 的解析逻辑内联进 core 的版本: +// - 依赖 core 自带的 markdown-it,不动 worktree 的 node_modules; +// - 入参 { configPath, packagesDir, docFileName, outPath } 全部可注入,可对任意 +// tag 的 worktree 现场生成 properties,供 build-meta 消费; +// - 修正了 3.x 版 create-properties.js 的 bug:taro 模式仍写死 ./properties.json +// 导致污染 H5 数据 —— 这里一律按 outPath 写。 +import fs from 'node:fs' +import path from 'node:path' +import MarkdownIt from 'markdown-it' + +const TBODY_OPEN = 'tbody_open' +const TBODY_CLOSE = 'tbody_close' +const TR_OPEN = 'tr_open' +const TR_CLOSE = 'tr_close' + +const md = new MarkdownIt() + +// 从 markdown token 流里逐个表格抽出 { h2, h3, list(每行的 token 片段) }。 +const gen = (tokens) => { + const getNextTable = (t, index) => { + const list = t.slice(index) + const start = list.findIndex((token) => token.type === TBODY_OPEN) + const end = list.findIndex((token) => token.type === TBODY_CLOSE) + return start === -1 || end === -1 ? [0, 0] : [index + start, index + end] + } + const parseTable = (t, left, right) => { + const sourcesMap = [] + const currSources = t.slice(left, right + 1) + while (currSources.filter((source) => source.type === TR_OPEN).length) { + const trStartIndex = currSources.findIndex((s) => s.type === TR_OPEN) + const trEndIndex = currSources.findIndex((s) => s.type === TR_CLOSE) + sourcesMap.push(currSources.slice(trStartIndex, trEndIndex + 1)) + currSources.splice(trStartIndex, trEndIndex - trStartIndex + 1) + } + return sourcesMap + } + const searchName = (t, left) => { + const list = t.slice(0, left).reverse() + const h2 = list.findIndex( + (token) => token.tag === 'h2' && token.type === 'heading_open' + ) + const h3 = list.findIndex( + (token) => token.tag === 'h3' && token.type === 'heading_open' + ) + return [list[h2 - 1]?.content || '', list[h3 - 1]?.content || ''] + } + const res = [] + let index = 0 + while (true) { + const [left, right] = getNextTable(tokens, index) + if (left >= right) break // 扫到文档结尾 + index = right + 1 + const [h2, h3] = searchName(tokens, left) + res.push({ h2, h3, list: parseTable(tokens, left, right) }) + } + return res +} + +const formatDefault = (str) => { + str = str.trim() + if (str[0] === '`') str = str.slice(1) + if (str[str.length - 1] === '`') str = str.slice(0, str.length - 1) + return str.trim() +} + +/** + * 生成 properties 数据(与仓库内 scripts/properties(.taro).json 结构一致)。 + * + * @param {object} opts + * @param {string} opts.configPath src/config.json 绝对路径 + * @param {string} opts.packagesDir src/packages 绝对路径 + * @param {string} opts.docFileName 'doc.md'(H5)| 'doc.taro.md'(Taro) + * @param {string} opts.outPath 输出 json 的绝对路径 + * @returns {{ count: number, componentsWithDoc: number }} + */ +export function runCreateProperties({ configPath, packagesDir, docFileName, outPath }) { + const cfg = JSON.parse(fs.readFileSync(configPath, 'utf-8')) + const typesData = [] + let componentsWithDoc = 0 + + for (const nav of cfg.nav) { + for (const component of nav.packages) { + const absolutePath = path.join( + packagesDir, + component.name.toLowerCase(), + docFileName + ) + if (!fs.existsSync(absolutePath)) continue + componentsWithDoc++ + const data = fs.readFileSync(absolutePath, 'utf8') + const sources = md.parse(data, {}) + const res = gen(sources) + res.forEach((r) => { + if (r.h2.includes('主题')) return + for (const sourceMap of r.list) { + const inlineItem = sourceMap.filter((s) => s.type === 'inline') + const propItem = + inlineItem.length > 0 + ? `${inlineItem[0].content.replace(/`.*?`/g, '')}` + : '' + const infoItem = inlineItem.length > 1 ? `${inlineItem[1].content}` : '' + const typeItem = inlineItem.length > 2 ? `${inlineItem[2].content}` : '' + let defaultItem = inlineItem.length > 3 ? `${inlineItem[3].content}` : '' + defaultItem = formatDefault(defaultItem) + typesData.push({ + 组件分类: nav.name, + 组件名: r.h2, + 版本号: component.version, + 表格名: r.h3, + 第一列: propItem, + 第二列: infoItem, + 第三列: typeItem, + 第四列: defaultItem, + }) + } + }) + } + } + + fs.mkdirSync(path.dirname(outPath), { recursive: true }) + // 关键修正:一律写 outPath(3.x 原脚本 taro 模式写死 ./properties.json,会污染 H5)。 + fs.writeFileSync(outPath, `${JSON.stringify(typesData, null, 2)}`) + return { count: typesData.length, componentsWithDoc } +} diff --git a/packages/nutui-react-cli-core/scripts/prepare-data.mjs b/packages/nutui-react-cli-core/scripts/prepare-data.mjs index 1fd34aa33f..5e6b82f030 100644 --- a/packages/nutui-react-cli-core/scripts/prepare-data.mjs +++ b/packages/nutui-react-cli-core/scripts/prepare-data.mjs @@ -1,63 +1,16 @@ -// 读仓库根 meta/components.json,为某个 CLI 叶子包产出自包含的 data/ 快照。 +// 把某个 tag 快照的 meta/components.json 裁剪为叶子包自包含的 data/v{tag}/ 目录树。 // 平台差异通过参数注入:H5 取 docs.h5/enUS + demos.h5 + api;Taro 取 docs.taro + demos.taro + apiTaro。 -// 会先自动重生成 meta(generate:meta),无需手动同步。 -// 各叶子包的 scripts/prepare-data.mjs 只需 import 本函数并传入自己的参数。 +// +// 数据来源改为「某个 worktree(tag checkout)+ 其 meta」而非仓库根,从而支持多版本: +// sync.mjs 为每个入选 tag 产出 worktree + meta,叶子包 prepare-data 循环调本函数把每份 +// 裁剪到 data/v{tag}/。 // DO NOT manual edit the output (data/). Run: pnpm run prepare-data import fs from 'node:fs' import path from 'node:path' -import { fileURLToPath } from 'node:url' -import { execFileSync } from 'node:child_process' -const __dirname = path.dirname(fileURLToPath(import.meta.url)) -// core 包目录:packages/nutui-react-cli-core/scripts -> .. -const CORE_DIR = path.resolve(__dirname, '..') -// 仓库根:packages/nutui-react-cli-core -> ../.. -const REPO_ROOT = path.resolve(CORE_DIR, '..', '..') -const META_PATH = path.join(REPO_ROOT, 'meta/components.json') -const BUILD_META_SCRIPT = path.join(REPO_ROOT, 'scripts/build-meta.mjs') - -// 先重生成 meta 再读,确保快照与仓库源码(config/properties/demos/...)同步, -// 免去手动 generate:meta 的漏同步风险。build-meta 纯读源码且幂等,可安全重复执行。 -function regenerateMeta() { - if (!fs.existsSync(BUILD_META_SCRIPT)) { - // 脱离 monorepo(如已发布包被重新 build),无源码可生成,沿用现有快照。 - console.log('ℹ️ 未找到 build-meta 脚本,跳过 meta 重生成(沿用现有快照)') - return - } - console.log('🔄 重新生成 meta/components.json(generate:meta)...') - execFileSync(process.execPath, [BUILD_META_SCRIPT], { stdio: 'inherit' }) -} - -function readMeta() { - try { - regenerateMeta() - } catch (err) { - // meta 生成失败:有旧快照则告警后沿用,否则无从继续。 - if (fs.existsSync(META_PATH)) { - console.warn( - `⚠️ meta 重生成失败(${err.message}),沿用现有 ${path.relative(REPO_ROOT, META_PATH)}` - ) - } else { - console.error( - `❌ meta 重生成失败且无现有快照:${err.message}\n` + - ` 请在仓库根手动执行:npm run generate:meta` - ) - process.exit(1) - } - } - if (!fs.existsSync(META_PATH)) { - console.error( - `❌ 未找到 ${path.relative(REPO_ROOT, META_PATH)}。\n` + - ` 它是构建产物,请先在仓库根执行:npm run generate:meta` - ) - process.exit(1) - } - return JSON.parse(fs.readFileSync(META_PATH, 'utf-8')) -} - -// meta 里的路径始终是 posix 相对仓库根路径,用 posix 取 basename,再 join 到本地。 -function copyRepoFile(relPosixPath, destAbs) { - const srcAbs = path.join(REPO_ROOT, relPosixPath) +// meta 里的路径始终是 posix 相对仓库根路径。从 srcRepoRoot(worktree)取源文件。 +function copyRepoFile(srcRepoRoot, relPosixPath, destAbs) { + const srcAbs = path.join(srcRepoRoot, relPosixPath) if (!fs.existsSync(srcAbs)) return false fs.mkdirSync(path.dirname(destAbs), { recursive: true }) fs.copyFileSync(srcAbs, destAbs) @@ -65,23 +18,25 @@ function copyRepoFile(relPosixPath, destAbs) { } /** - * 生成某叶子包的 data/ 快照。 + * 把一份 tag 快照裁剪进叶子包的某个版本目录。 * @param {object} opts - * @param {string} opts.pkgDir 叶子包根目录绝对路径(其下写入 data/) - * @param {Record} opts.docKeys lang -> meta.docs 的 key。如 {zh:'h5', en:'enUS'} 或 {zh:'taro'} + * @param {string} opts.srcMetaPath 该 tag 的 meta/components.json 绝对路径(sync 产出) + * @param {string} opts.srcRepoRoot 该 tag 的 worktree 根(复制 docs/demos 源文件用) + * @param {string} opts.outDir 输出目录(如 /data/v3.1.0),内部写 meta.json + docs/ + demos/ + * @param {Record} opts.docKeys lang -> meta.docs 的 key。{zh:'h5', en:'enUS'} 或 {zh:'taro'} * @param {'h5'|'taro'} opts.demoKey 取 meta.demos 的哪一端 * @param {'api'|'apiTaro'} opts.apiField 取哪一端的 API 表,归一写入快照的 `api` 字段 + * @param {Record} [opts.migrationDocs] "from-v{n}" -> 该 tag worktree 内迁移文档的 posix 相对路径。 + * 如 { 'from-v3': 'src/sites/sites-react/doc/docs/react/migrate-from-v3.md' }。文档只在部分 tag(如 v4)存在, + * 不存在时静默跳过。命中则复制到 outDir/migrations/from-v{n}.md,供 migrate 命令读取。 + * @returns {{ docCount:number, demoCount:number, missing:number, libVersion:string, migrationCount:number }} */ -export function prepareData({ pkgDir, docKeys, demoKey, apiField }) { - console.log( - `🚀 building data snapshot (${apiField === 'apiTaro' ? 'Taro' : 'React/H5'}) for ${path.relative(REPO_ROOT, pkgDir)} ...` - ) - const meta = readMeta() - const DATA_DIR = path.join(pkgDir, 'data') +export function prepareData({ srcMetaPath, srcRepoRoot, outDir, docKeys, demoKey, apiField, migrationDocs }) { + const meta = JSON.parse(fs.readFileSync(srcMetaPath, 'utf-8')) - // 全量重建,避免残留上一版删掉的组件。 - fs.rmSync(DATA_DIR, { recursive: true, force: true }) - fs.mkdirSync(DATA_DIR, { recursive: true }) + // 全量重建该版本目录,避免残留上一次的组件。 + fs.rmSync(outDir, { recursive: true, force: true }) + fs.mkdirSync(outDir, { recursive: true }) // 1. meta.json:把对应端的 API 归一到 `api`,删除 apiTaro,缩减 docs/demos 到本端, // 使运行时代码只读 `api` / 只需 .md,无需感知平台。 @@ -89,7 +44,6 @@ export function prepareData({ pkgDir, docKeys, demoKey, apiField }) { const slimComponents = {} for (const [id, c] of Object.entries(meta.components)) { const api = c[apiField] || { tables: [] } - // docs 归一:只保留本端支持的 lang,key 换成 lang 名(zh/en),值为原相对路径(仅用于存在性)。 const docs = {} for (const lang of langKeys) docs[lang] = c.docs?.[docKeys[lang]] ?? null slimComponents[id] = { @@ -102,7 +56,7 @@ export function prepareData({ pkgDir, docKeys, demoKey, apiField }) { } const slimMeta = { ...meta, components: slimComponents } fs.writeFileSync( - path.join(DATA_DIR, 'meta.json'), + path.join(outDir, 'meta.json'), `${JSON.stringify(slimMeta, null, 2)}\n` ) @@ -111,30 +65,87 @@ export function prepareData({ pkgDir, docKeys, demoKey, apiField }) { const missing = [] for (const c of Object.values(meta.components)) { - // 2. docs:按 lang 复制为 data/docs//.md(zh.md / en.md)。 + // 2. docs:按 lang 复制为 docs//.md(zh.md / en.md)。 for (const lang of langKeys) { const rel = c.docs && c.docs[docKeys[lang]] if (!rel) continue - const ok = copyRepoFile(rel, path.join(DATA_DIR, 'docs', c.id, `${lang}.md`)) + const ok = copyRepoFile(srcRepoRoot, rel, path.join(outDir, 'docs', c.id, `${lang}.md`)) if (ok) docCount++ else missing.push(rel) } - // 3. demos:扁平放到 data/demos//.tsx(无 platform 子层)。 + // 3. demos:扁平放到 demos//.tsx(无 platform 子层)。 const demoList = (c.demos && c.demos[demoKey]) || [] for (const rel of demoList) { const base = path.posix.basename(rel) - const ok = copyRepoFile(rel, path.join(DATA_DIR, 'demos', c.id, base)) + const ok = copyRepoFile(srcRepoRoot, rel, path.join(outDir, 'demos', c.id, base)) if (ok) demoCount++ else missing.push(rel) } } - console.log(`✅ 写入 ${path.relative(pkgDir, DATA_DIR)}/`) - console.log(` meta.json + docs ${docCount} 份 + demos ${demoCount} 份`) - if (missing.length) { - console.log(`\n⚠️ ${missing.length} 个 meta 引用的文件在仓库中缺失(已跳过):`) - for (const m of missing.slice(0, 20)) console.log(` - ${m}`) - if (missing.length > 20) console.log(` … 其余 ${missing.length - 20} 条省略`) + // 4. 迁移文档:把该 tag worktree 内存在的迁移文档复制到 outDir/migrations/from-v{n}.md。 + // 文档随 v4 beta 手写维护、只在部分 tag 存在(v3 tag 无),缺失静默跳过。 + let migrationCount = 0 + for (const [key, rel] of Object.entries(migrationDocs || {})) { + const ok = copyRepoFile(srcRepoRoot, rel, path.join(outDir, 'migrations', `${key}.md`)) + if (ok) migrationCount++ + } + + return { docCount, demoCount, missing: missing.length, libVersion: meta.libVersion, missingList: missing, migrationCount } +} + +/** + * 完整多版本快照构建:调 sync 遍历 tag,把每份裁剪到 data/v{tag}/,写 data/versions.json。 + * 供两个叶子包的 scripts/prepare-data.mjs 复用(只传自己的平台参数)。 + * + * @param {object} opts + * @param {string} opts.pkgDir 叶子包根目录绝对路径(其下写 data/) + * @param {Record} opts.docKeys + * @param {'h5'|'taro'} opts.demoKey + * @param {'api'|'apiTaro'} opts.apiField + * @param {Record} [opts.migrationDocs] "from-v{n}" -> 迁移文档相对仓库根的 posix 路径(按平台不同) + */ +export async function prepareAllVersions({ pkgDir, docKeys, demoKey, apiField, migrationDocs }) { + const { sync } = await import('./sync.mjs') + const platform = apiField === 'apiTaro' ? 'Taro' : 'React/H5' + console.log(`🚀 building multi-version data snapshots (${platform}) for ${pkgDir} ...\n`) + + const { snapshots, versionsIndex, cleanup } = sync() + const DATA_DIR = path.join(pkgDir, 'data') + + try { + // 全量重建 data/:清掉旧的 v*/ 目录与 versions.json,避免残留已下线的版本。 + fs.rmSync(DATA_DIR, { recursive: true, force: true }) + fs.mkdirSync(DATA_DIR, { recursive: true }) + + for (const snap of snapshots) { + const outDir = path.join(DATA_DIR, `v${snap.clean}`) + const res = prepareData({ + srcMetaPath: snap.metaPath, + srcRepoRoot: snap.worktreeDir, + outDir, + docKeys, + demoKey, + apiField, + migrationDocs, + }) + console.log( + ` ✅ v${snap.clean}: docs ${res.docCount} + demos ${res.demoCount}${res.migrationCount ? ` + 迁移文档 ${res.migrationCount}` : ''}(libVersion ${res.libVersion})` + ) + if (res.missing) { + console.log(` ⚠️ ${res.missing} 个文件在 worktree 缺失(已跳过)`) + } + } + + // versions.json + fs.writeFileSync( + path.join(DATA_DIR, 'versions.json'), + `${JSON.stringify(versionsIndex, null, 2)}\n` + ) + console.log(`\n✅ 写入 data/versions.json(defaultMajor=${versionsIndex.defaultMajor})`) + } finally { + cleanup() + console.log('🧹 已清理临时 worktree') } } diff --git a/packages/nutui-react-cli-core/scripts/sync.mjs b/packages/nutui-react-cli-core/scripts/sync.mjs new file mode 100644 index 0000000000..7f25235318 --- /dev/null +++ b/packages/nutui-react-cli-core/scripts/sync.mjs @@ -0,0 +1,294 @@ +// 多版本数据同步:遍历 NutUI git tag,为每个「每 minor 最高 patch」的 tag 生成一份 +// meta/components.json 快照(放在该 tag 的 worktree 内),并产出 versions.json 索引。 +// +// 与 antd 的 scripts/sync.ts 对应,差异: +// - NutUI 的随包数据是「meta.json + docs/ + demos/ 目录树」而非单个大 JSON,故 sync +// 只负责产出 worktree + meta,真正裁剪到叶子包 data/ 由 prepare-data 消费; +// - properties 一律现场重生成(runCreateProperties):实测 3.x 已提交的 +// scripts/properties.json 是滞后产物(少 200+ 条、API 覆盖率更低),现补更准; +// - worktree 的清理交回调用方(prepare-data 复制完源文件后再 cleanup)。 +// +// sync() 返回 { snapshots, versionsIndex, cleanup }: +// snapshots: Array<{ tag, major, worktreeDir, metaPath }> 已按 tag 升序 +// versionsIndex: { defaultMajor, majors: { vX: { latest, stable, minors } } } +// cleanup(): 移除所有本次创建的 worktree +import fs from 'node:fs' +import os from 'node:os' +import path from 'node:path' +import { fileURLToPath } from 'node:url' +import { execFileSync } from 'node:child_process' +import versionsConfig from './versions.config.mjs' +import { buildMeta } from './build-meta.mjs' +import { runCreateProperties } from './create-properties.mjs' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) +// packages/nutui-react-cli-core/scripts -> 仓库根(用于 git 命令的 cwd) +const REPO_ROOT = path.resolve(__dirname, '..', '..', '..') + +const git = (args, opts = {}) => + execFileSync('git', args, { encoding: 'utf-8', ...opts }) + +// 拉取远端全部 v3/v4 tag(升序)。只列 refs,不落地对象。 +function listRemoteTags(repo) { + const out = git([ + 'ls-remote', + '--tags', + '--sort=v:refname', + repo, + 'refs/tags/v3*', + 'refs/tags/v4*', + ]) + return out + .split('\n') + .filter((line) => line && !line.includes('^{}')) + .map((line) => line.replace(/.*refs\/tags\//, '')) +} + +// tag 串(可能带 v 前缀)→ { major:'v3', minor:'3.0', patch:0, prerelease:bool, raw } +function parseTag(tag) { + const clean = tag.replace(/^v/, '') + const m = clean.match(/^(\d+)\.(\d+)\.(\d+)(?:-(.+))?$/) + if (!m) return null + return { + raw: tag, + clean, + majorNum: Number(m[1]), + major: `v${m[1]}`, + minor: `${m[1]}.${m[2]}`, + patch: Number(m[3]), + prerelease: m[4] != null, + } +} + +// 每 minor 取最高 patch。默认排除预发布;includePrerelease 为 true 时(如 v4 全 beta) +// 在「该 minor 没有稳定 tag」时才纳入最高的预发布。 +function buildMinorMap(tags, includePrerelease) { + const parsed = tags.map(parseTag).filter(Boolean) + const byMinor = new Map() // minor -> best parsed tag + const better = (a, b) => { + // 稳定优先于预发布;同稳定性比 patch;patch 相同再比预发布串(beta.7 > beta.6) + if (a.prerelease !== b.prerelease) return a.prerelease ? b : a + if (a.patch !== b.patch) return a.patch > b.patch ? a : b + return (a.raw > b.raw ? a : b) + } + for (const t of parsed) { + if (t.prerelease && !includePrerelease) continue + const cur = byMinor.get(t.minor) + byMinor.set(t.minor, cur ? better(cur, t) : t) + } + return byMinor +} + +// 确保本地存在该 tag 对象;没有就从远端 fetch 到本地同名 tag(幂等、无害)。 +function ensureTagLocally(repo, tag) { + try { + git(['rev-parse', '--verify', '--quiet', `refs/tags/${tag}`], { + cwd: REPO_ROOT, + stdio: 'pipe', + }) + return + } catch { + // 本地无此 tag + } + console.log(` fetch tag ${tag} ...`) + git(['fetch', repo, `refs/tags/${tag}:refs/tags/${tag}`], { + cwd: REPO_ROOT, + stdio: 'pipe', + }) +} + +// 为 tag 建 detached worktree,返回其路径。 +function addWorktree(tag) { + const dir = path.join(os.tmpdir(), `nutui-sync-${tag.replace(/[^\w.-]/g, '_')}`) + if (fs.existsSync(dir)) { + // 残留则先移除(--force 容忍脏树) + try { + git(['worktree', 'remove', dir, '--force'], { cwd: REPO_ROOT, stdio: 'pipe' }) + } catch { + fs.rmSync(dir, { recursive: true, force: true }) + } + } + git(['worktree', 'add', '--detach', dir, tag], { cwd: REPO_ROOT, stdio: 'pipe' }) + return dir +} + +// worktree 内路径解析(3.x / 4.x 结构一致)。 +function repoPaths(worktreeDir) { + return { + configPath: path.join(worktreeDir, 'src/config.json'), + packagesDir: path.join(worktreeDir, 'src/packages'), + variablesPath: path.join(worktreeDir, 'src/styles/variables.scss'), + pkgJsonPath: path.join(worktreeDir, 'package.json'), + specDir: path.join(worktreeDir, 'src/types/spec'), + propertiesPath: path.join(worktreeDir, 'scripts/properties.json'), + propertiesTaroPath: path.join(worktreeDir, 'scripts/properties-taro.json'), + metaPath: path.join(worktreeDir, 'meta/components.json'), + } +} + +// 现补 properties(H5 必补;Taro 仅在有 doc.taro.md 时补,否则降级为空表 + warn)。 +function preparePropertiesInWorktree(worktreeDir, p) { + const hasTaroDoc = () => { + // 抽查任一组件是否有 doc.taro.md;有就认为该 tag 支持 taro 端抽取。 + try { + const cfg = JSON.parse(fs.readFileSync(p.configPath, 'utf-8')) + for (const nav of cfg.nav) { + for (const c of nav.packages) { + const f = path.join(p.packagesDir, c.name.toLowerCase(), 'doc.taro.md') + if (fs.existsSync(f)) return true + } + } + } catch { + /* ignore */ + } + return false + } + + runCreateProperties({ + configPath: p.configPath, + packagesDir: p.packagesDir, + docFileName: 'doc.md', + outPath: p.propertiesPath, + }) + + if (hasTaroDoc()) { + runCreateProperties({ + configPath: p.configPath, + packagesDir: p.packagesDir, + docFileName: 'doc.taro.md', + outPath: p.propertiesTaroPath, + }) + } else { + console.log(` ⚠️ 该 tag 无 doc.taro.md,apiTaro 将为空表`) + } +} + +/** + * 执行多版本同步。 + * @param {object} [opts] + * @param {(tag:string)=>boolean} [opts.tagFilter] 只处理返回 true 的 tag(调试用) + * @returns {{ snapshots: Array<{tag,major,worktreeDir,metaPath}>, versionsIndex: object, cleanup: ()=>void }} + */ +export function sync(opts = {}) { + const cfg = versionsConfig + const allTags = listRemoteTags(cfg.repo) + console.log(`🔎 远端 tag 共 ${allTags.length} 个`) + + // 按 major 分桶取每 minor 最高 patch + const majorKeys = Object.keys(cfg.majors) // ['v3','v4'] + const selected = [] // { tag, major, minor } + const versionsIndex = { defaultMajor: cfg.defaultMajor, majors: {} } + + for (const majorKey of majorKeys) { + const majorNum = Number(majorKey.replace('v', '')) + const majorCfg = cfg.majors[majorKey] + const tagsOfMajor = allTags.filter((t) => { + const p = parseTag(t) + return p && p.majorNum === majorNum + }) + const minorMap = buildMinorMap(tagsOfMajor, majorCfg.includePrerelease) + if (!minorMap.size) { + console.warn(` ⚠️ ${majorKey} 无入选 tag,跳过`) + continue + } + // minors 索引(不带 v 前缀)+ latest(最高 minor 的 tag) + const minors = {} + let latest = null + const sortedMinors = [...minorMap.keys()].sort( + (a, b) => Number(a.split('.')[1]) - Number(b.split('.')[1]) + ) + for (const minor of sortedMinors) { + const t = minorMap.get(minor) + minors[minor] = t.clean + selected.push({ tag: t.raw, clean: t.clean, major: majorKey, minor }) + latest = t.clean + } + versionsIndex.majors[majorKey] = { + latest, + stable: majorCfg.stable !== false && sortedMinors.some((mn) => !minorMap.get(mn).prerelease), + minors, + } + } + + // 可选过滤(调试:只跑部分 tag) + const finalSelected = opts.tagFilter + ? selected.filter((s) => opts.tagFilter(s.tag)) + : selected + + console.log( + `📦 入选快照 ${finalSelected.length} 个:${finalSelected.map((s) => s.tag).join(', ')}` + ) + + const createdWorktrees = [] + const snapshots = [] + + for (const sel of finalSelected) { + console.log(`\n=== ${sel.tag}(${sel.major})===`) + ensureTagLocally(cfg.repo, sel.tag) + const worktreeDir = addWorktree(sel.tag) + createdWorktrees.push(worktreeDir) + const p = repoPaths(worktreeDir) + + preparePropertiesInWorktree(worktreeDir, p) + + const res = buildMeta({ + repoRoot: worktreeDir, + configPath: p.configPath, + propertiesPath: p.propertiesPath, + propertiesTaroPath: p.propertiesTaroPath, + packagesDir: p.packagesDir, + variablesPath: p.variablesPath, + pkgJsonPath: p.pkgJsonPath, + specDir: p.specDir, + cssVarPrefix: cfg.majors[sel.major].cssVarPrefix, + out: p.metaPath, + }) + console.log( + ` ✅ meta:组件 ${res.componentCount},有 API(H5) ${res.apiComponentCount},token ${res.globalTokens},libVersion ${res.libVersion}` + ) + + // 校验:meta 必须有 libVersion 且组件非空 + if (!res.libVersion || res.componentCount === 0) { + throw new Error(`${sel.tag} 的 meta 校验失败:libVersion=${res.libVersion} componentCount=${res.componentCount}`) + } + + snapshots.push({ + tag: sel.tag, + clean: sel.clean, + major: sel.major, + worktreeDir, + metaPath: p.metaPath, + }) + } + + const cleanup = () => { + for (const dir of createdWorktrees) { + try { + git(['worktree', 'remove', dir, '--force'], { cwd: REPO_ROOT, stdio: 'pipe' }) + } catch { + fs.rmSync(dir, { recursive: true, force: true }) + } + } + } + + return { snapshots, versionsIndex, cleanup } +} + +// 独立调试:node scripts/sync.mjs [tagSubstr] +function main() { + const filterArg = process.argv[2] + const { snapshots, versionsIndex, cleanup } = sync( + filterArg ? { tagFilter: (t) => t.includes(filterArg) } : {} + ) + console.log('\n=== versions.json ===') + console.log(JSON.stringify(versionsIndex, null, 2)) + console.log(`\n快照 ${snapshots.length} 个(worktree 未清理,调试模式保留)`) + if (process.env.NUTUI_SYNC_CLEANUP === '1') { + cleanup() + console.log('已清理 worktree') + } +} + +if (process.argv[1] && fileURLToPath(import.meta.url) === path.resolve(process.argv[1])) { + main() +} diff --git a/packages/nutui-react-cli-core/scripts/versions.config.mjs b/packages/nutui-react-cli-core/scripts/versions.config.mjs new file mode 100644 index 0000000000..ae55238edb --- /dev/null +++ b/packages/nutui-react-cli-core/scripts/versions.config.mjs @@ -0,0 +1,18 @@ +// 多版本 sync 的配置:数据源仓库、默认 major、各 major 的抽取参数。 +// CLI 仓库自带(不随叶子包发布)。sync.mjs 读取它遍历 tag 生成快照。 +// +// 说明: +// - repo 用 SSH。实测本机 https://github.com 不通、git@github.com 可达;SSH 也便于 +// 在开发机复用已配置的凭证。CI 上若走 HTTPS,改这里即可。 +// - cssVarPrefix:v3 与 v4 的组件 token 体系实测均为 --nutui-(3.x 的 variables.scss +// 有 900+ 行 --nutui-,仅个位数 --nut-scale-* 工具变量不带 !default、不计入 token), +// 故两者都用 --nutui-。保留该字段仅为未来某个 major 若切换前缀时的扩展点。 +// - includePrerelease:v4 目前只有 beta tag,需允许预发布才能取到 latest。 +export default { + repo: 'git@github.com:jdf2e/nutui-react.git', + defaultMajor: 'v4', + majors: { + v3: { cssVarPrefix: '--nutui-', includePrerelease: false, stable: true }, + v4: { cssVarPrefix: '--nutui-', includePrerelease: true, stable: false }, + }, +} diff --git a/packages/nutui-react-cli-core/src/__tests__/diff-compute.test.ts b/packages/nutui-react-cli-core/src/__tests__/diff-compute.test.ts new file mode 100644 index 0000000000..efbd9dc384 --- /dev/null +++ b/packages/nutui-react-cli-core/src/__tests__/diff-compute.test.ts @@ -0,0 +1,107 @@ +import { describe, expect, it } from 'vitest' +import { diffComponent, diffMeta } from '../commands/diff-compute.js' +import type { ApiTable, Component, Meta } from '../types.js' + +// 造一个只含 props 表的最小 Component。 +function comp(id: string, name: string, rows: ApiTable['rows'], subComponent: string | null = null): Component { + return { + id, + name, + cName: name, + version: '0', + category: { name: '', enName: '' }, + taro: false, + v15: false, + v16: false, + docs: { h5: null, enUS: null, zhTW: null, taro: null }, + demos: { h5: [], taro: [] }, + api: { + tables: [{ name: 'Props', kind: 'props', sourceComponent: id, subComponent, rows }], + }, + tokens: [], + } +} + +function meta(components: Record, libVersion = '0'): Meta { + return { + schemaVersion: '1', + libVersion, + componentCount: Object.keys(components).length, + apiComponentCount: 0, + categories: [], + globalTokens: [], + components, + } +} + +describe('diffComponent', () => { + it('检测默认值与类型变更(Empty size/status 场景)', () => { + const v1 = comp('empty', 'Empty', [ + { prop: 'size', desc: '', type: '`small` | `base`', default: 'base' }, + { prop: 'status', desc: '', type: '`empty` | `error`', default: 'empty' }, + ]) + const v2 = comp('empty', 'Empty', [ + { prop: 'size', desc: '', type: '`full` | `half`', default: 'half' }, + { prop: 'status', desc: '', type: '`network`', default: 'network' }, + ]) + const d = diffComponent('Empty', v1, v2) + expect(d.added).toEqual([]) + expect(d.removed).toEqual([]) + expect(d.changed).toHaveLength(2) + const size = d.changed.find((c) => c.prop === 'size')! + expect(size).toMatchObject({ defaultFrom: 'base', defaultTo: 'half' }) + expect(size.typeFrom).toBe('`small` | `base`') + expect(size.typeTo).toBe('`full` | `half`') + }) + + it('检测新增与移除的 prop', () => { + const v1 = comp('x', 'X', [{ prop: 'old', desc: '', type: 'string', default: '' }]) + const v2 = comp('x', 'X', [{ prop: 'new', desc: '', type: 'string', default: '' }]) + const d = diffComponent('X', v1, v2) + expect(d.added.map((r) => r.prop)).toEqual(['new']) + expect(d.removed.map((r) => r.prop)).toEqual(['old']) + expect(d.changed).toEqual([]) + }) + + it('完全一致时无差异', () => { + const rows = [{ prop: 'a', desc: '', type: 'string', default: '1' }] + const d = diffComponent('X', comp('x', 'X', rows), comp('x', 'X', rows)) + expect(d.added).toEqual([]) + expect(d.removed).toEqual([]) + expect(d.changed).toEqual([]) + }) + + it('组件仅在一版存在(v2 新增组件):全部记为 added', () => { + const v2 = comp('n', 'New', [{ prop: 'a', desc: '', type: 'string', default: '' }]) + const d = diffComponent('New', undefined, v2) + expect(d.added).toHaveLength(1) + }) + + it('子组件表的 prop 以 Sub.prop 作 key,不与主表混淆', () => { + const v1 = comp('x', 'X', [{ prop: 'value', desc: '', type: 'string', default: '' }], 'Item') + const v2 = comp('x', 'X', [{ prop: 'value', desc: '', type: 'number', default: '' }], 'Item') + const d = diffComponent('X', v1, v2) + expect(d.changed[0].prop).toBe('Item.value') + }) +}) + +describe('diffMeta', () => { + const metaA = meta({ + empty: comp('empty', 'Empty', [{ prop: 'size', desc: '', type: 's', default: 'base' }]), + button: comp('button', 'Button', [{ prop: 'type', desc: '', type: 's', default: '' }]), + }, '3.1.0') + const metaB = meta({ + empty: comp('empty', 'Empty', [{ prop: 'size', desc: '', type: 's', default: 'half' }]), + button: comp('button', 'Button', [{ prop: 'type', desc: '', type: 's', default: '' }]), + }, '4.0.0') + + it('只返回有差异的组件(Button 无变化被过滤)', () => { + const diffs = diffMeta(metaA, metaB) + expect(diffs.map((d) => d.name)).toEqual(['Empty']) + }) + + it('指定组件只 diff 该组件', () => { + const diffs = diffMeta(metaA, metaB, 'button') + expect(diffs).toEqual([]) + }) +}) diff --git a/packages/nutui-react-cli-core/src/__tests__/migrate-parse.test.ts b/packages/nutui-react-cli-core/src/__tests__/migrate-parse.test.ts new file mode 100644 index 0000000000..968def0771 --- /dev/null +++ b/packages/nutui-react-cli-core/src/__tests__/migrate-parse.test.ts @@ -0,0 +1,94 @@ +import { describe, expect, it } from 'vitest' +import { parseMigrationDoc, filterSections } from '../commands/migrate-parse.js' + +const DOC = `# 从 v3 升级到 v4 + +本文档将帮助您升级。 + +## 升级步骤 + +1. 安装 v4 + +--- + +## 不兼容更新与兼容升级说明 + +### Toast (操作反馈) + +- 默认 duration 由 2s 调整为 3s。 + +### Empty (反馈类) + +> v4 不提供 v3 兼容。 + +- size 默认值由 base 调整为 half。 + +### Popover (反馈类) + +- theme 默认值由 light 调整为 dark。 +` + +describe('parseMigrationDoc', () => { + const parsed = parseMigrationDoc(DOC) + + it('抽取主标题', () => { + expect(parsed.title).toBe('从 v3 升级到 v4') + }) + + it('intro 含升级步骤、且止于第一个 ### 之前', () => { + expect(parsed.intro).toContain('## 升级步骤') + expect(parsed.intro).toContain('安装 v4') + expect(parsed.intro).not.toContain('### Toast') + }) + + it('切出全部组件段', () => { + expect(parsed.sections.map((s) => s.component)).toEqual([ + 'Toast', + 'Empty', + 'Popover', + ]) + }) + + it('解析组件分类', () => { + expect(parsed.sections[0]).toMatchObject({ + component: 'Toast', + category: '操作反馈', + }) + }) + + it('组件段 raw 含标题行与正文,不含下一段', () => { + const empty = parsed.sections.find((s) => s.component === 'Empty')! + expect(empty.raw).toContain('### Empty (反馈类)') + expect(empty.raw).toContain('size 默认值由 base 调整为 half') + expect(empty.raw).not.toContain('### Popover') + }) + + it('无 ### 段的文档:sections 空、intro 为全文', () => { + const p = parseMigrationDoc('# 标题\n\n正文无组件段。') + expect(p.sections).toEqual([]) + expect(p.intro).toContain('正文无组件段') + }) +}) + +describe('filterSections', () => { + const { sections } = parseMigrationDoc(DOC) + + it('空 names 返回全部', () => { + expect(filterSections(sections, [])).toHaveLength(3) + }) + + it('大小写不敏感过滤单组件', () => { + const r = filterSections(sections, ['empty']) + expect(r).toHaveLength(1) + expect(r[0].component).toBe('Empty') + }) + + it('多组件过滤', () => { + const r = filterSections(sections, ['Toast', 'Popover']) + expect(r.map((s) => s.component)).toEqual(['Toast', 'Popover']) + }) + + it('未命中返回空', () => { + expect(filterSections(sections, ['Button'])).toEqual([]) + }) +}) diff --git a/packages/nutui-react-cli-core/src/__tests__/scan.test.ts b/packages/nutui-react-cli-core/src/__tests__/scan.test.ts new file mode 100644 index 0000000000..13065cde57 --- /dev/null +++ b/packages/nutui-react-cli-core/src/__tests__/scan.test.ts @@ -0,0 +1,87 @@ +import { afterAll, beforeAll, describe, expect, it } from 'vitest' +import fs from 'node:fs' +import os from 'node:os' +import path from 'node:path' +import { extractComponents, scanProject } from '../utils/scan.js' + +describe('extractComponents', () => { + it('解析单行具名 import', () => { + const c = extractComponents(`import { Button, Cell } from '@nutui/nutui-react'`) + expect(c.sort()).toEqual(['Button', 'Cell']) + }) + + it('解析多行 import', () => { + const c = extractComponents(`import { + Button, + Empty, + Toast, + } from '@nutui/nutui-react'`) + expect(c.sort()).toEqual(['Button', 'Empty', 'Toast']) + }) + + it('识别 -taro 包与 icons 包', () => { + const src = ` + import { Cell } from '@nutui/nutui-react-taro' + import { Dongdong } from '@nutui/icons-react-taro' + ` + expect(extractComponents(src).sort()).toEqual(['Cell', 'Dongdong']) + }) + + it('as 别名取原名,type 前缀剥除', () => { + const c = extractComponents( + `import { Button as Btn, type ButtonProps } from '@nutui/nutui-react'` + ) + expect(c.sort()).toEqual(['Button', 'ButtonProps']) + }) + + it('忽略非 NutUI import', () => { + const c = extractComponents(`import { useState } from 'react'`) + expect(c).toEqual([]) + }) + + it('不误伤子路径 import(如 dist/style)', () => { + // 具名 import 才计入;副作用 import '@nutui/nutui-react/dist/style.css' 无具名列表 + const c = extractComponents(`import '@nutui/nutui-react/dist/style.css'`) + expect(c).toEqual([]) + }) +}) + +describe('scanProject', () => { + let dir: string + beforeAll(() => { + dir = fs.mkdtempSync(path.join(os.tmpdir(), 'nutui-scan-')) + fs.mkdirSync(path.join(dir, 'src'), { recursive: true }) + fs.writeFileSync( + path.join(dir, 'src', 'a.tsx'), + `import { Button, Empty } from '@nutui/nutui-react'` + ) + fs.writeFileSync( + path.join(dir, 'src', 'b.ts'), + `import { Cell } from '@nutui/nutui-react'` + ) + // node_modules 应被跳过 + fs.mkdirSync(path.join(dir, 'node_modules'), { recursive: true }) + fs.writeFileSync( + path.join(dir, 'node_modules', 'x.tsx'), + `import { ShouldSkip } from '@nutui/nutui-react'` + ) + }) + afterAll(() => fs.rmSync(dir, { recursive: true, force: true })) + + it('聚合去重、跳过 node_modules', () => { + const r = scanProject(dir) + expect(r.components).toEqual(['Button', 'Cell', 'Empty']) + expect(r.components).not.toContain('ShouldSkip') + }) + + it('files 只含有 NutUI import 的文件', () => { + const r = scanProject(dir) + expect(r.files).toHaveLength(2) + }) + + it('传入单文件也可扫描', () => { + const r = scanProject(path.join(dir, 'src', 'a.tsx')) + expect(r.components).toEqual(['Button', 'Empty']) + expect(r.scannedFileCount).toBe(1) + }) +}) diff --git a/packages/nutui-react-cli-core/src/__tests__/version-routing.test.ts b/packages/nutui-react-cli-core/src/__tests__/version-routing.test.ts new file mode 100644 index 0000000000..c0ee2280eb --- /dev/null +++ b/packages/nutui-react-cli-core/src/__tests__/version-routing.test.ts @@ -0,0 +1,149 @@ +import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' +import fs from 'node:fs' +import os from 'node:os' +import path from 'node:path' +import { resolveSnapshotDir } from '../data.js' +import { detectVersion } from '../version.js' +import type { VersionsIndex } from '../types.js' + +// 手造一个多版本 data 目录:versions.json + 各 v{tag}/meta.json(仅需能被 existsSync 命中)。 +const VERSIONS: VersionsIndex = { + defaultMajor: 'v4', + majors: { + v3: { latest: '3.1.0', stable: true, minors: { '3.0': '3.0.20', '3.1': '3.1.0' } }, + v4: { latest: '4.0.0-beta.7', stable: false, minors: { '4.0': '4.0.0-beta.7' } }, + }, +} + +let dataDir: string + +beforeAll(() => { + dataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'nutui-cli-test-')) + fs.writeFileSync( + path.join(dataDir, 'versions.json'), + JSON.stringify(VERSIONS, null, 2) + ) + for (const tag of ['3.0.20', '3.1.0', '4.0.0-beta.7']) { + const dir = path.join(dataDir, `v${tag}`) + fs.mkdirSync(dir, { recursive: true }) + fs.writeFileSync( + path.join(dir, 'meta.json'), + JSON.stringify({ libVersion: tag, components: {} }) + ) + } +}) + +afterAll(() => { + fs.rmSync(dataDir, { recursive: true, force: true }) +}) + +describe('resolveSnapshotDir', () => { + it('精确 minor 命中:3.1.0 → v3.1.0', () => { + expect(resolveSnapshotDir(dataDir, '3.1.0')).toBe(path.join(dataDir, 'v3.1.0')) + }) + + it('最近不超过的旧 minor:3.0.5 → v3.0.20(minor 3.0)', () => { + expect(resolveSnapshotDir(dataDir, '3.0.5')).toBe(path.join(dataDir, 'v3.0.20')) + }) + + it('请求的 minor 高于已有:3.5.0 → 回退最近的 3.1 → v3.1.0', () => { + expect(resolveSnapshotDir(dataDir, '3.5.0')).toBe(path.join(dataDir, 'v3.1.0')) + }) + + it('v4 预发布精确命中:4.0.0-beta.7 → v4.0.0-beta.7', () => { + expect(resolveSnapshotDir(dataDir, '4.0.0-beta.7')).toBe( + path.join(dataDir, 'v4.0.0-beta.7') + ) + }) + + it('未知 major:99.0.0 → 回退 defaultMajor(v4) latest,并 stderr 提示', () => { + const spy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true) + expect(resolveSnapshotDir(dataDir, '99.0.0')).toBe( + path.join(dataDir, 'v4.0.0-beta.7') + ) + expect(spy).toHaveBeenCalledOnce() + spy.mockRestore() + }) + + it('只给 major:3 → v3 latest(v3.1.0)', () => { + expect(resolveSnapshotDir(dataDir, '3')).toBe(path.join(dataDir, 'v3.1.0')) + }) +}) + +describe('detectVersion', () => { + const base = { npmPackageName: '@nutui/nutui-react', versionsIndex: VERSIONS } + + it('flag 优先,保留预发布:4.0.0-beta.7', () => { + const r = detectVersion({ ...base, flag: '4.0.0-beta.7' }) + expect(r).toMatchObject({ version: '4.0.0-beta.7', major: 'v4', source: 'flag' }) + }) + + it('flag 只给 major:3 → coerce 3.0.0', () => { + const r = detectVersion({ ...base, flag: '3' }) + expect(r).toMatchObject({ version: '3.0.0', major: 'v3', source: 'flag' }) + }) + + it('flag 带 range 前缀:^3.1.0 → coerce 3.1.0', () => { + const r = detectVersion({ ...base, flag: '^3.1.0' }) + expect(r).toMatchObject({ version: '3.1.0', major: 'v3', source: 'flag' }) + }) + + it('flag 的 major 不存在:99 → 回退 defaultMajor v4 latest', () => { + const spy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true) + const r = detectVersion({ ...base, flag: '99.0.0' }) + expect(r).toMatchObject({ version: '4.0.0-beta.7', major: 'v4' }) + spy.mockRestore() + }) + + it('无 flag、无项目文件:fallback 到 defaultMajor latest', () => { + const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'nutui-empty-')) + const r = detectVersion({ ...base, cwd: tmp }) + expect(r).toMatchObject({ + version: '4.0.0-beta.7', + major: 'v4', + source: 'fallback', + }) + fs.rmSync(tmp, { recursive: true, force: true }) + }) + + it('node_modules 检测优先于 package.json', () => { + const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'nutui-proj-')) + fs.mkdirSync(path.join(tmp, 'node_modules/@nutui/nutui-react'), { + recursive: true, + }) + fs.writeFileSync( + path.join(tmp, 'node_modules/@nutui/nutui-react/package.json'), + JSON.stringify({ version: '3.0.9' }) + ) + fs.writeFileSync( + path.join(tmp, 'package.json'), + JSON.stringify({ dependencies: { '@nutui/nutui-react': '^4.0.0-beta.7' } }) + ) + const r = detectVersion({ ...base, cwd: tmp }) + expect(r).toMatchObject({ version: '3.0.9', major: 'v3', source: 'node_modules' }) + fs.rmSync(tmp, { recursive: true, force: true }) + }) + + it('无 node_modules 时读 package.json 依赖声明', () => { + const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'nutui-proj2-')) + fs.writeFileSync( + path.join(tmp, 'package.json'), + JSON.stringify({ devDependencies: { '@nutui/nutui-react': '~3.1.0' } }) + ) + const r = detectVersion({ ...base, cwd: tmp }) + expect(r).toMatchObject({ version: '3.1.0', major: 'v3', source: 'package.json' }) + fs.rmSync(tmp, { recursive: true, force: true }) + }) + + it('非 semver 依赖声明(workspace:*)→ fallback', () => { + const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'nutui-proj3-')) + fs.writeFileSync( + path.join(tmp, 'package.json'), + JSON.stringify({ dependencies: { '@nutui/nutui-react': 'workspace:*' } }) + ) + const r = detectVersion({ ...base, cwd: tmp }) + expect(r.source).toBe('fallback') + expect(r.major).toBe('v4') + fs.rmSync(tmp, { recursive: true, force: true }) + }) +}) diff --git a/packages/nutui-react-cli-core/src/cli.ts b/packages/nutui-react-cli-core/src/cli.ts index 56de9c4f13..3d34cb8b33 100644 --- a/packages/nutui-react-cli-core/src/cli.ts +++ b/packages/nutui-react-cli-core/src/cli.ts @@ -5,6 +5,8 @@ import { runInfo } from './commands/info.js' import { runDoc } from './commands/doc.js' import { runDemo } from './commands/demo.js' import { runToken } from './commands/token.js' +import { runMigrate } from './commands/migrate.js' +import { runDiff } from './commands/diff.js' import { runMcp } from './commands/mcp.js' import type { CliConfig } from './config.js' import type { Lang, OutputFormat } from './types.js' @@ -25,6 +27,13 @@ export function runCli(config: CliConfig, argv: string[]): void { default: 'text' as OutputFormat, global: true, }) + .option('nutui-version', { + alias: 'nv', + type: 'string', + describe: + '目标 NutUI 版本(如 3、3.1.0、4.0.0-beta.7);省略则从 node_modules / package.json 自动检测', + global: true, + }) .command( ['list', 'ls'], '列出全部组件(按分类)', @@ -39,6 +48,7 @@ export function runCli(config: CliConfig, argv: string[]): void { config, category: a.category, format: a.format as OutputFormat, + nutuiVersion: a['nutui-version'] as string | undefined, }) ) .command( @@ -55,6 +65,7 @@ export function runCli(config: CliConfig, argv: string[]): void { config, component: a.component as string, format: a.format as OutputFormat, + nutuiVersion: a['nutui-version'] as string | undefined, }) ) .command( @@ -82,6 +93,7 @@ export function runCli(config: CliConfig, argv: string[]): void { component: a.component as string, lang: (a.lang as Lang) ?? config.defaultLang, format: a.format as OutputFormat, + nutuiVersion: a['nutui-version'] as string | undefined, }) ) .command( @@ -104,6 +116,7 @@ export function runCli(config: CliConfig, argv: string[]): void { component: a.component as string, name: a.name as string | undefined, format: a.format as OutputFormat, + nutuiVersion: a['nutui-version'] as string | undefined, }) ) .command( @@ -119,6 +132,67 @@ export function runCli(config: CliConfig, argv: string[]): void { config, component: a.component as string | undefined, format: a.format as OutputFormat, + nutuiVersion: a['nutui-version'] as string | undefined, + }) + ) + .command( + 'migrate [from] [to]', + '输出 vN→vM 迁移指南(默认 3 4);--component 看单组件,--apply 扫描项目生成迁移提示', + (y) => + y + .positional('from', { + type: 'string', + describe: '起始大版本(如 3),默认 3', + }) + .positional('to', { + type: 'string', + describe: '目标大版本(如 4),默认 from+1', + }) + .option('component', { + alias: 'c', + type: 'string', + describe: '只输出该组件的迁移说明(大小写不敏感)', + }) + .option('apply', { + type: 'string', + describe: '扫描该目录,只输出项目用到组件的迁移步骤并生成给 Agent 的提示', + }), + (a) => + runMigrate({ + config, + from: a.from as string | undefined, + to: a.to as string | undefined, + component: a.component as string | undefined, + apply: a.apply as string | undefined, + format: a.format as OutputFormat, + }) + ) + .command( + 'diff [component]', + '跨版本 Props 差异比对(新增 / 移除 / 类型或默认值变更)', + (y) => + y + .positional('v1', { + type: 'string', + describe: '起始版本(如 3、3.1.0)', + demandOption: true, + }) + .positional('v2', { + type: 'string', + describe: '目标版本(如 4、4.0.0-beta.7)', + demandOption: true, + }) + .positional('component', { + type: 'string', + describe: '只比对该组件(大小写不敏感);省略则比对全部', + }), + (a) => + runDiff({ + config, + v1: a.v1 as string, + v2: a.v2 as string, + component: a.component as string | undefined, + format: a.format as OutputFormat, }) ) .command( diff --git a/packages/nutui-react-cli-core/src/commands/_shared.ts b/packages/nutui-react-cli-core/src/commands/_shared.ts index 86a89fd3be..71498854c5 100644 --- a/packages/nutui-react-cli-core/src/commands/_shared.ts +++ b/packages/nutui-react-cli-core/src/commands/_shared.ts @@ -1,7 +1,60 @@ -// 命令间共享:解析组件名,未命中则打印 did-you-mean 并退出。 -import { resolveComponent, suggestComponents } from '../data.js' +// 命令间共享:版本检测 + 快照解析,以及组件名解析(未命中打印 did-you-mean 并退出)。 +import { + loadMetaByDir, + loadVersionsIndex, + resolveComponent, + resolveSnapshotDir, + suggestComponents, +} from '../data.js' +import { detectVersion, type VersionInfo } from '../version.js' import type { CliConfig } from '../config.js' -import type { Component, Meta } from '../types.js' +import type { Component, Meta, OutputFormat } from '../types.js' + +export interface ResolvedContext { + meta: Meta + snapshotDir: string + versionInfo: VersionInfo +} + +// 命令入口统一走这里:flag → detectVersion → resolveSnapshotDir → loadMetaByDir。 +export function resolveContext( + config: CliConfig, + nutuiVersion?: string +): ResolvedContext { + const versionsIndex = loadVersionsIndex(config.dataDir) + const versionInfo = detectVersion({ + flag: nutuiVersion, + cwd: process.cwd(), + npmPackageName: config.npmPackageName, + versionsIndex, + }) + const snapshotDir = resolveSnapshotDir(config.dataDir, versionInfo.version) + const meta = loadMetaByDir(snapshotDir) + return { meta, snapshotDir, versionInfo } +} + +// 文本格式的版本信息头,如 "NutUI React(H5)v3.1.0(来源:package.json)"。 +export function versionHeaderText( + config: CliConfig, + info: VersionInfo +): string { + const sourceLabel: Record = { + flag: '--nutui-version', + node_modules: 'node_modules', + 'package.json': 'package.json', + fallback: '默认', + } + return `${config.libLabel} v${info.version}(来源:${sourceLabel[info.source]})` +} + +// JSON 格式的 _meta 字段。 +export function versionMeta(info: VersionInfo): { + version: string + major: string + source: VersionInfo['source'] +} { + return { version: info.version, major: info.major, source: info.source } +} export function resolveOrExit( config: CliConfig, @@ -17,3 +70,6 @@ export function resolveOrExit( process.stderr.write(`${msg}\n`) process.exit(1) } + +// 各命令共用的输出格式类型别名(避免重复 import)。 +export type { OutputFormat } diff --git a/packages/nutui-react-cli-core/src/commands/demo.ts b/packages/nutui-react-cli-core/src/commands/demo.ts index 5fe6be72c9..cc4b0db3fb 100644 --- a/packages/nutui-react-cli-core/src/commands/demo.ts +++ b/packages/nutui-react-cli-core/src/commands/demo.ts @@ -1,40 +1,52 @@ // demo [name] —— 列出或输出 demo 源码。 -import { listDemos, loadMeta, readDemo } from '../data.js' +import { listDemos, readDemo } from '../data.js' import { output } from '../format.js' -import { resolveOrExit } from './_shared.js' +import { + resolveContext, + resolveOrExit, + versionHeaderText, + versionMeta, + type OutputFormat, +} from './_shared.js' import type { CliConfig } from '../config.js' -import type { OutputFormat } from '../types.js' export interface DemoArgs { config: CliConfig component: string name?: string format: OutputFormat + nutuiVersion?: string } export function runDemo(args: DemoArgs): void { const { config } = args - const meta = loadMeta(config.dataDir) + const { meta, snapshotDir, versionInfo } = resolveContext( + config, + args.nutuiVersion + ) const comp = resolveOrExit(config, meta, args.component) - const demos = listDemos(config.dataDir, comp) + const demos = listDemos(snapshotDir, comp) - // 无 name:列出全部 demo 文件名。 + // 无 name:列出全部 demo 文件名(列表带版本头,便于确认版本)。 if (!args.name) { const jsonData = { id: comp.id, name: comp.name, demos } - output(args.format, jsonData, () => { - if (!demos.length) - return `${comp.name} ${comp.cName} 暂无 ${config.demoLabel} 示例。` - return `${comp.name} ${comp.cName} 的 ${config.demoLabel} 示例(${demos.length} 个):\n${demos - .map((d) => ` - ${d}`) - .join( - '\n' - )}\n\n查看源码:${config.binName} demo ${comp.name} ${demos[0]}` - }) + output( + args.format, + jsonData, + () => { + if (!demos.length) + return `${comp.name} ${comp.cName} 暂无 ${config.demoLabel} 示例。` + return `${comp.name} ${comp.cName} 的 ${config.demoLabel} 示例(${demos.length} 个):\n${demos + .map((d) => ` - ${d}`) + .join('\n')}\n\n查看源码:${config.binName} demo ${comp.name} ${demos[0]}` + }, + { header: versionHeaderText(config, versionInfo), meta: versionMeta(versionInfo) } + ) return } - // 有 name:输出该 demo 源码。 - const code = readDemo(config.dataDir, comp, args.name) + // 有 name:输出该 demo 源码(正文是源码,text 不加版本头,json 带 _meta)。 + const code = readDemo(snapshotDir, comp, args.name) if (code === null) { const jsonData = { id: comp.id, @@ -42,16 +54,24 @@ export function runDemo(args: DemoArgs): void { demo: args.name, code: null, } - output(args.format, jsonData, () => { - const avail = demos.length - ? `可选:${demos.join(' / ')}` - : `该组件暂无 ${config.demoLabel} 示例` - return `未找到示例「${args.name}」。${avail}` - }) + output( + args.format, + jsonData, + () => { + const avail = demos.length + ? `可选:${demos.join(' / ')}` + : `该组件暂无 ${config.demoLabel} 示例` + return `未找到示例「${args.name}」。${avail}` + }, + { header: '', meta: versionMeta(versionInfo) } + ) process.exitCode = 1 return } const jsonData = { id: comp.id, name: comp.name, demo: args.name, code } - output(args.format, jsonData, () => code) + output(args.format, jsonData, () => code, { + header: '', + meta: versionMeta(versionInfo), + }) } diff --git a/packages/nutui-react-cli-core/src/commands/diff-compute.ts b/packages/nutui-react-cli-core/src/commands/diff-compute.ts new file mode 100644 index 0000000000..7e5dbeb1c5 --- /dev/null +++ b/packages/nutui-react-cli-core/src/commands/diff-compute.ts @@ -0,0 +1,99 @@ +// 跨版本 API 差异比对:给定两份 meta,逐组件比对 Props 表,找出 added / removed / changed。 +// 纯函数,不碰 IO,便于单测。diff 命令负责加载两版快照后调用它。 +import type { ApiRow, ApiTable, Component, Meta } from '../types.js' + +export interface RowChange { + prop: string + // 变化维度:type 或 default 变了就记,各记 旧→新 + typeFrom?: string + typeTo?: string + defaultFrom?: string + defaultTo?: string +} + +export interface ComponentDiff { + name: string + // v2 相对 v1 新增的 Props({prop, desc, type, default}) + added: ApiRow[] + // v2 相对 v1 移除的 Props + removed: ApiRow[] + // 两版都在、但 type / default 有变化 + changed: RowChange[] +} + +// 把组件的所有 props 表拍平成 prop → row(同名 prop 以先出现者为准;子组件表也并入)。 +function flattenProps(comp: Component | undefined): Map { + const map = new Map() + if (!comp) return map + const tables: ApiTable[] = comp.api?.tables ?? [] + for (const t of tables) { + if (t.kind !== 'props') continue + for (const r of t.rows) { + // 子组件表用 "Sub.prop" 作 key,避免与主表同名 prop 混淆 + const key = t.subComponent ? `${t.subComponent}.${r.prop}` : r.prop + if (!map.has(key)) map.set(key, r) + } + } + return map +} + +// 比对单个组件在两版之间的 Props 差异。 +export function diffComponent( + name: string, + v1: Component | undefined, + v2: Component | undefined +): ComponentDiff { + const a = flattenProps(v1) + const b = flattenProps(v2) + + const added: ApiRow[] = [] + const removed: ApiRow[] = [] + const changed: RowChange[] = [] + + for (const [key, row] of b) { + if (!a.has(key)) added.push(row) + } + for (const [key, row] of a) { + if (!b.has(key)) removed.push(row) + } + for (const [key, ra] of a) { + const rb = b.get(key) + if (!rb) continue + const c: RowChange = { prop: key } + let dirty = false + if ((ra.type ?? '') !== (rb.type ?? '')) { + c.typeFrom = ra.type + c.typeTo = rb.type + dirty = true + } + if ((ra.default ?? '') !== (rb.default ?? '')) { + c.defaultFrom = ra.default + c.defaultTo = rb.default + dirty = true + } + if (dirty) changed.push(c) + } + + return { name, added, removed, changed } +} + +// 比对两份 meta 的所有组件(或指定单组件)。返回有差异的组件列表。 +export function diffMeta( + metaA: Meta, + metaB: Meta, + componentId?: string +): ComponentDiff[] { + const ids = componentId + ? [componentId.toLowerCase()] + : [...new Set([...Object.keys(metaA.components), ...Object.keys(metaB.components)])].sort() + + const out: ComponentDiff[] = [] + for (const id of ids) { + const ca = metaA.components[id] + const cb = metaB.components[id] + const name = cb?.name ?? ca?.name ?? id + const d = diffComponent(name, ca, cb) + if (d.added.length || d.removed.length || d.changed.length) out.push(d) + } + return out +} diff --git a/packages/nutui-react-cli-core/src/commands/diff.ts b/packages/nutui-react-cli-core/src/commands/diff.ts new file mode 100644 index 0000000000..56a23b21e3 --- /dev/null +++ b/packages/nutui-react-cli-core/src/commands/diff.ts @@ -0,0 +1,110 @@ +// diff [component] —— 跨版本 Props 差异比对。 +// 与其它命令不同:需要同时加载两个版本的快照,故绕过只解析单版本的 resolveContext, +// 直接用 resolveSnapshotDir + loadMetaByDir 各加载一份,再交给 diff-compute 纯函数比对。 +import { loadMetaByDir, resolveComponent, resolveSnapshotDir, suggestComponents } from '../data.js' +import { output, renderTable } from '../format.js' +import { diffMeta, type ComponentDiff } from './diff-compute.js' +import type { OutputFormat } from './_shared.js' +import type { CliConfig } from '../config.js' + +export interface DiffArgs { + config: CliConfig + v1: string + v2: string + component?: string + format: OutputFormat +} + +function fail(msg: string): never { + process.stderr.write(`${msg}\n`) + process.exit(1) +} + +export function runDiff(args: DiffArgs): void { + const dirA = resolveSnapshotDir(args.config.dataDir, args.v1) + const dirB = resolveSnapshotDir(args.config.dataDir, args.v2) + const metaA = loadMetaByDir(dirA) + const metaB = loadMetaByDir(dirB) + + // 组件名 → id:以 v2(较新版本)为准解析;未命中给 did-you-mean。 + let componentId: string | undefined + if (args.component) { + const comp = resolveComponent(metaB, args.component) ?? resolveComponent(metaA, args.component) + if (!comp) { + const sug = suggestComponents(metaB, args.component) + fail( + `未找到组件「${args.component}」。${sug.length ? ` 你是否想找:${sug.join(' / ')}?` : ''}` + ) + } + componentId = comp.id + } + + const diffs = diffMeta(metaA, metaB, componentId) + + const jsonData = { + v1: metaA.libVersion, + v2: metaB.libVersion, + component: args.component ?? null, + changedComponentCount: diffs.length, + components: diffs.map((d) => ({ + name: d.name, + added: d.added, + removed: d.removed, + changed: d.changed, + })), + } + + output( + args.format, + jsonData, + () => renderText(metaA.libVersion, metaB.libVersion, diffs, args.component), + { header: '', meta: { v1: metaA.libVersion, v2: metaB.libVersion } } + ) +} + +function renderText( + v1: string, + v2: string, + diffs: ComponentDiff[], + component: string | undefined +): string { + const head = `NutUI Props 差异:v${v1} → v${v2}` + if (!diffs.length) { + return `${head}\n\n${component ? `组件 ${component} 在两版之间无 Props 差异。` : '两版之间无 Props 级差异。'}` + } + + const blocks: string[] = [head] + for (const d of diffs) { + const lines: string[] = [`▍${d.name}`] + if (d.added.length) { + lines.push(' 新增:') + lines.push( + indent(renderTable(['属性', '类型', '默认值'], d.added.map((r) => [r.prop, r.type, r.default]))) + ) + } + if (d.removed.length) { + lines.push(' 移除:') + lines.push( + indent(renderTable(['属性', '类型', '默认值'], d.removed.map((r) => [r.prop, r.type, r.default]))) + ) + } + if (d.changed.length) { + lines.push(' 变更:') + const rows = d.changed.map((c) => [ + c.prop, + c.typeFrom !== undefined ? `${c.typeFrom} → ${c.typeTo}` : '—', + c.defaultFrom !== undefined ? `${c.defaultFrom} → ${c.defaultTo}` : '—', + ]) + lines.push(indent(renderTable(['属性', '类型', '默认值'], rows))) + } + blocks.push(lines.join('\n')) + } + return blocks.join('\n\n') +} + +function indent(s: string): string { + return s + .split('\n') + .map((l) => ` ${l}`) + .join('\n') +} diff --git a/packages/nutui-react-cli-core/src/commands/doc.ts b/packages/nutui-react-cli-core/src/commands/doc.ts index 2cbf623176..897041bf0e 100644 --- a/packages/nutui-react-cli-core/src/commands/doc.ts +++ b/packages/nutui-react-cli-core/src/commands/doc.ts @@ -1,23 +1,34 @@ // doc [--lang ...] —— 输出组件完整文档原文。 -import { loadMeta, readDoc } from '../data.js' +import { readDoc } from '../data.js' import { output } from '../format.js' -import { resolveOrExit } from './_shared.js' +import { + resolveContext, + resolveOrExit, + versionMeta, + type OutputFormat, +} from './_shared.js' import type { CliConfig } from '../config.js' -import type { Lang, OutputFormat } from '../types.js' +import type { Lang } from '../types.js' export interface DocArgs { config: CliConfig component: string lang: Lang format: OutputFormat + nutuiVersion?: string } export function runDoc(args: DocArgs): void { - const meta = loadMeta(args.config.dataDir) + const { meta, snapshotDir, versionInfo } = resolveContext( + args.config, + args.nutuiVersion + ) const comp = resolveOrExit(args.config, meta, args.component) - const content = readDoc(args.config.dataDir, comp, args.lang) + const content = readDoc(snapshotDir, comp, args.lang) const langName = args.config.langLabel[args.lang] ?? args.lang + // 文档正文是原始 markdown,text 格式不加版本头以免污染可直接保存的内容; + // json 格式仍在 _meta 里带版本信息。 if (content === null) { const jsonData = { id: comp.id, @@ -25,12 +36,18 @@ export function runDoc(args: DocArgs): void { lang: args.lang, content: null, } - output(args.format, jsonData, () => { - return `${comp.name} ${comp.cName} 暂无${langName}文档(可能是隐藏子组件)。` - }) + output( + args.format, + jsonData, + () => `${comp.name} ${comp.cName} 暂无${langName}文档(可能是隐藏子组件)。`, + { header: '', meta: versionMeta(versionInfo) } + ) return } const jsonData = { id: comp.id, name: comp.name, lang: args.lang, content } - output(args.format, jsonData, () => content) + output(args.format, jsonData, () => content, { + header: '', + meta: versionMeta(versionInfo), + }) } diff --git a/packages/nutui-react-cli-core/src/commands/info.ts b/packages/nutui-react-cli-core/src/commands/info.ts index 43b6d85d8c..a5b9b088d3 100644 --- a/packages/nutui-react-cli-core/src/commands/info.ts +++ b/packages/nutui-react-cli-core/src/commands/info.ts @@ -1,18 +1,23 @@ // info —— 输出组件 Props 表。 -import { loadMeta } from '../data.js' import { output, renderTable } from '../format.js' -import { resolveOrExit } from './_shared.js' +import { + resolveContext, + resolveOrExit, + versionHeaderText, + versionMeta, + type OutputFormat, +} from './_shared.js' import type { CliConfig } from '../config.js' -import type { OutputFormat } from '../types.js' export interface InfoArgs { config: CliConfig component: string format: OutputFormat + nutuiVersion?: string } export function runInfo(args: InfoArgs): void { - const meta = loadMeta(args.config.dataDir) + const { meta, versionInfo } = resolveContext(args.config, args.nutuiVersion) const comp = resolveOrExit(args.config, meta, args.component) const tables = comp.api?.tables ?? [] const propTables = tables.filter((t) => t.kind === 'props') @@ -29,22 +34,27 @@ export function runInfo(args: InfoArgs): void { })), } - output(args.format, jsonData, () => { - if (!propTables.length) { - const others = tables.map((t) => `${t.name}(${t.kind})`).join('、') - let msg = `${comp.name} ${comp.cName} 没有 Props 表` - msg += others - ? `。它可能是布局/子组件;现有其它表:${others}。` - : `(可能是布局或纯样式组件)。` - return msg - } - const blocks = propTables.map((t) => { - const title = t.subComponent - ? `${t.name}(${comp.name}.${t.subComponent})` - : t.name - const rows = t.rows.map((r) => [r.prop, r.desc, r.type, r.default]) - return `▍${title}\n${renderTable(['属性', '说明', '类型', '默认值'], rows)}` - }) - return `${comp.name} ${comp.cName}(v${comp.version})\n\n${blocks.join('\n\n')}` - }) + output( + args.format, + jsonData, + () => { + if (!propTables.length) { + const others = tables.map((t) => `${t.name}(${t.kind})`).join('、') + let msg = `${comp.name} ${comp.cName} 没有 Props 表` + msg += others + ? `。它可能是布局/子组件;现有其它表:${others}。` + : `(可能是布局或纯样式组件)。` + return msg + } + const blocks = propTables.map((t) => { + const title = t.subComponent + ? `${t.name}(${comp.name}.${t.subComponent})` + : t.name + const rows = t.rows.map((r) => [r.prop, r.desc, r.type, r.default]) + return `▍${title}\n${renderTable(['属性', '说明', '类型', '默认值'], rows)}` + }) + return `${comp.name} ${comp.cName}(v${comp.version})\n\n${blocks.join('\n\n')}` + }, + { header: versionHeaderText(args.config, versionInfo), meta: versionMeta(versionInfo) } + ) } diff --git a/packages/nutui-react-cli-core/src/commands/list.ts b/packages/nutui-react-cli-core/src/commands/list.ts index 2b593ed97b..e853734ce7 100644 --- a/packages/nutui-react-cli-core/src/commands/list.ts +++ b/packages/nutui-react-cli-core/src/commands/list.ts @@ -1,17 +1,22 @@ // list —— 按分类列出组件。 -import { loadMeta } from '../data.js' import { output, renderTable } from '../format.js' +import { + resolveContext, + versionHeaderText, + versionMeta, + type OutputFormat, +} from './_shared.js' import type { CliConfig } from '../config.js' -import type { OutputFormat } from '../types.js' export interface ListArgs { config: CliConfig category?: string format: OutputFormat + nutuiVersion?: string } export function runList(args: ListArgs): void { - const meta = loadMeta(args.config.dataDir) + const { meta, versionInfo } = resolveContext(args.config, args.nutuiVersion) let categories = meta.categories if (args.category) { const key = args.category.toLowerCase() @@ -39,20 +44,25 @@ export function runList(args: ListArgs): void { })), })) - output(args.format, jsonData, () => { - const blocks: string[] = [] - let total = 0 - for (const cat of categories) { - const comps = cat.components - .map((id) => meta.components[id]) - .filter(Boolean) - total += comps.length - const rows = comps.map((c) => [c.name, c.cName ?? '', c.version ?? '']) - blocks.push( - `▍${cat.name} (${cat.enName})\n${renderTable(['组件', '中文名', '版本'], rows)}` - ) - } - const header = `${args.config.libLabel}共 ${meta.componentCount} 个组件,版本 ${meta.libVersion}` - return `${header}\n\n${blocks.join('\n\n')}\n\n合计:${total} 个` - }) + output( + args.format, + { categories: jsonData }, + () => { + const blocks: string[] = [] + let total = 0 + for (const cat of categories) { + const comps = cat.components + .map((id) => meta.components[id]) + .filter(Boolean) + total += comps.length + const rows = comps.map((c) => [c.name, c.cName ?? '', c.version ?? '']) + blocks.push( + `▍${cat.name} (${cat.enName})\n${renderTable(['组件', '中文名', '版本'], rows)}` + ) + } + const header = `${args.config.libLabel}共 ${meta.componentCount} 个组件,版本 ${meta.libVersion}` + return `${header}\n\n${blocks.join('\n\n')}\n\n合计:${total} 个` + }, + { header: versionHeaderText(args.config, versionInfo), meta: versionMeta(versionInfo) } + ) } diff --git a/packages/nutui-react-cli-core/src/commands/migrate-parse.ts b/packages/nutui-react-cli-core/src/commands/migrate-parse.ts new file mode 100644 index 0000000000..d27bf2dc37 --- /dev/null +++ b/packages/nutui-react-cli-core/src/commands/migrate-parse.ts @@ -0,0 +1,76 @@ +// 迁移文档解析:把手写的 migrate-from-v{n}.md 切成"通用引言 + 每组件段落",供 migrate 命令按组件过滤。 +// 文档是散文式 markdown:# 标题 → 若干前置 ## 小节(升级步骤等)→ 逐组件的 ### Component (分类) 段。 +// 解析只做结构切分,不重写内容——组件段原文照出,交由 Agent 阅读判断(见 SKILL)。 + +export interface MigrationSection { + // 组件英文名(### 标题里的首个 token,如 Toast / Empty) + component: string + // 组件中文分类标注(### 标题括号内文本,如"操作反馈";无则空串) + category: string + // 该组件段的原文 markdown(含 ### 标题行) + raw: string +} + +export interface ParsedMigration { + // 文档主标题(# 之后的文本,如"从 v3 升级到 v4") + title: string + // 第一个 ### 组件段之前的全部内容(含"升级步骤"等通用小节) + intro: string + // 逐组件段落 + sections: MigrationSection[] +} + +// 解析 ### 标题行:`### Toast (操作反馈)` → { component:'Toast', category:'操作反馈' }。 +// 容忍全角/半角括号与缺省分类。 +function parseHeading(line: string): { component: string; category: string } { + const text = line.replace(/^###\s+/, '').trim() + const m = text.match(/^(\S+)\s*[((]\s*(.*?)\s*[))]\s*$/) + if (m) return { component: m[1], category: m[2] } + // 没有括号分类,取首个 token 作组件名 + return { component: text.split(/\s+/)[0] ?? text, category: '' } +} + +export function parseMigrationDoc(md: string): ParsedMigration { + const lines = md.split('\n') + + // 主标题:首个 `# ` + let title = '' + for (const l of lines) { + const m = l.match(/^#\s+(.+)$/) + if (m) { + title = m[1].trim() + break + } + } + + // 找出所有 `### ` 组件段的起始行号 + const headingIdx: number[] = [] + lines.forEach((l, i) => { + if (/^###\s+/.test(l)) headingIdx.push(i) + }) + + // intro = 第一个 ### 之前的全部(去尾部空白) + const introEnd = headingIdx.length ? headingIdx[0] : lines.length + const intro = lines.slice(0, introEnd).join('\n').trim() + + const sections: MigrationSection[] = [] + for (let k = 0; k < headingIdx.length; k++) { + const start = headingIdx[k] + const end = k + 1 < headingIdx.length ? headingIdx[k + 1] : lines.length + const raw = lines.slice(start, end).join('\n').trim() + const { component, category } = parseHeading(lines[start]) + sections.push({ component, category, raw }) + } + + return { title, intro, sections } +} + +// 大小写不敏感地按组件名筛选段落。names 为空时返回全部。 +export function filterSections( + sections: MigrationSection[], + names: string[] +): MigrationSection[] { + if (!names.length) return sections + const want = new Set(names.map((n) => n.toLowerCase())) + return sections.filter((s) => want.has(s.component.toLowerCase())) +} diff --git a/packages/nutui-react-cli-core/src/commands/migrate.ts b/packages/nutui-react-cli-core/src/commands/migrate.ts new file mode 100644 index 0000000000..9426efa932 --- /dev/null +++ b/packages/nutui-react-cli-core/src/commands/migrate.ts @@ -0,0 +1,169 @@ +// migrate [from] [to] —— 输出 vN→vM 迁移指南。数据源是随包抽取的官方迁移文档(migrate-from-v{n}.md), +// 按组件切段后原文输出;--component 只看某组件,--apply 扫描项目、只输出用到组件的迁移步骤并生成给 Agent 的提示。 +// +// 与其它命令不同:迁移跨两个版本,故不走 resolveContext(它只解析单版本),而是用 to 版本定位快照、读其内迁移文档。 +import { loadVersionsIndex, readMigrationDoc, resolveSnapshotDir } from '../data.js' +import { output } from '../format.js' +import { parseMigrationDoc, filterSections } from './migrate-parse.js' +import { scanProject } from '../utils/scan.js' +import type { OutputFormat } from './_shared.js' +import type { CliConfig } from '../config.js' + +export interface MigrateArgs { + config: CliConfig + from?: string + to?: string + component?: string + apply?: string + format: OutputFormat +} + +// 把版本参数('3' / '3.1.0' / 'v4')归一为大版本号整数。无法解析返回 null。 +function toMajorNum(v: string | undefined): number | null { + if (!v) return null + const m = String(v).replace(/^v/, '').match(/^(\d+)/) + return m ? Number(m[1]) : null +} + +function fail(msg: string): never { + process.stderr.write(`${msg}\n`) + process.exit(1) +} + +export function runMigrate(args: MigrateArgs): void { + const fromMajor = toMajorNum(args.from) ?? 3 + const toMajor = toMajorNum(args.to) ?? fromMajor + 1 + + if (toMajor <= fromMajor) { + fail( + `迁移方向无效:from=v${fromMajor} to=v${toMajor}。请指定从低到高的大版本,如 ${args.config.binName} migrate 3 4。` + ) + } + + // 用 to 大版本定位快照目录(该版本快照里带 migrations/from-v{from}.md)。 + const idx = loadVersionsIndex(args.config.dataDir) + const toMajorKey = `v${toMajor}` + if (!idx.majors[toMajorKey]) { + fail( + `未找到 NutUI v${toMajor} 的离线数据,无法提供 v${fromMajor}→v${toMajor} 迁移指南。已知版本:${Object.keys(idx.majors).join(' / ')}。` + ) + } + const snapshotDir = resolveSnapshotDir(args.config.dataDir, String(toMajor)) + const md = readMigrationDoc(snapshotDir, fromMajor) + + if (md === null) { + fail( + `暂无 v${fromMajor}→v${toMajor} 的迁移文档数据。目前仅内置随 ${idx.majors[toMajorKey].latest} 分发的迁移说明;若为源码开发,请先运行 pnpm run build 重新抽取。` + ) + } + + const parsed = parseMigrationDoc(md) + + // 确定要输出的组件段:--apply 扫描项目取交集;--component 显式过滤;否则全部。 + let sections = parsed.sections + let scan: ReturnType | null = null + let matchedComponents: string[] = [] + let missingFromDoc: string[] = [] + + if (args.apply) { + scan = scanProject(args.apply) + sections = filterSections(parsed.sections, scan.components) + matchedComponents = sections.map((s) => s.component) + // 项目用到、但迁移文档未覆盖的组件(v3→v4 大多无破坏性变更,属正常)。 + const covered = new Set(sections.map((s) => s.component.toLowerCase())) + missingFromDoc = scan.components.filter((c) => !covered.has(c.toLowerCase())) + } else if (args.component) { + sections = filterSections(parsed.sections, [args.component]) + if (!sections.length) { + fail( + `迁移文档中未找到组件「${args.component}」。已覆盖:${parsed.sections.map((s) => s.component).join(' / ')}。` + ) + } + } + + const jsonData = { + from: `v${fromMajor}`, + to: `v${toMajor}`, + libVersion: idx.majors[toMajorKey].latest, + coveredComponents: parsed.sections.map((s) => s.component), + steps: sections.map((s) => ({ + component: s.component, + category: s.category, + guide: s.raw, + })), + ...(scan + ? { + appliedTo: args.apply, + scannedFiles: scan.scannedFileCount, + projectComponents: scan.components, + matchedComponents, + componentsWithoutBreakingChanges: missingFromDoc, + } + : {}), + } + + output( + args.format, + jsonData, + () => renderText(parsed, sections, args, scan, matchedComponents, missingFromDoc), + // 迁移指南本身是可直接粘贴的 markdown,text 不加版本头(同 doc.ts)。 + { header: '', meta: { from: `v${fromMajor}`, to: `v${toMajor}`, libVersion: idx.majors[toMajorKey].latest } } + ) +} + +function renderText( + parsed: ReturnType, + sections: ReturnType['sections'], + args: MigrateArgs, + scan: ReturnType | null, + matchedComponents: string[], + missingFromDoc: string[] +): string { + const parts: string[] = [] + + if (scan) { + // --apply:产出一段给 Code Agent 的定向迁移提示。 + parts.push(`# NutUI ${parsed.title || '版本迁移'}(基于项目扫描)`) + parts.push( + `> 扫描目录 \`${args.apply}\`:共 ${scan.scannedFileCount} 个源文件,检测到 ${scan.components.length} 个 NutUI 组件:${scan.components.join('、') || '(无)'}。` + ) + if (matchedComponents.length) { + parts.push( + `> 其中 **${matchedComponents.length}** 个有破坏性变更需处理:${matchedComponents.join('、')}。` + ) + } else { + parts.push('> 未命中任何有破坏性变更的组件——按当前迁移文档,项目所用组件无需改代码(仍建议构建后人工核对样式)。') + } + if (missingFromDoc.length) { + parts.push( + `> 以下组件项目用到、但迁移文档未列破坏性变更(v3→v4 多数组件如此,通常无需改动):${missingFromDoc.join('、')}。` + ) + } + parts.push( + [ + '## 给代码 Agent 的指令', + '', + '1. 仅需处理下方列出的组件,逐个阅读其「迁移说明」原文。', + '2. 破坏性变更集中在**样式 / CSS 类名 / Design Token / 枚举值 / 默认值**,几乎没有 Props 增删——请重点检查这些维度,不要臆造 Props 改名。', + '3. 枚举值/默认值变更(如 Empty 的 `size`/`status`、Popover 的 `theme`)**不会自动兼容**,必须改代码。', + '4. 对无法机械改写、需要视觉确认的改动,改完后标记「待人工复核」,不要声称已完成。', + '5. 迁移后运行构建验证;编译通过 ≠ 视觉/行为正确。', + ].join('\n') + ) + } else { + // 全量 / --component:先给引言(升级步骤),再给组件段。 + if (!args.component && parsed.intro) parts.push(parsed.intro) + } + + if (sections.length) { + if (scan || args.component) { + parts.push('---') + parts.push('## 迁移说明') + } + for (const s of sections) parts.push(s.raw) + } else if (!scan) { + parts.push('(无匹配的组件迁移说明。)') + } + + return parts.join('\n\n') +} diff --git a/packages/nutui-react-cli-core/src/commands/token.ts b/packages/nutui-react-cli-core/src/commands/token.ts index f5860b82f3..f1fd40ffc1 100644 --- a/packages/nutui-react-cli-core/src/commands/token.ts +++ b/packages/nutui-react-cli-core/src/commands/token.ts @@ -1,27 +1,41 @@ // token [Component] —— 输出 Design Token。无参列全局 token,有参列组件 token。 -import { loadMeta } from '../data.js' import { output, renderTable } from '../format.js' -import { resolveOrExit } from './_shared.js' +import { + resolveContext, + resolveOrExit, + versionHeaderText, + versionMeta, + type OutputFormat, +} from './_shared.js' import type { CliConfig } from '../config.js' -import type { OutputFormat, Token } from '../types.js' +import type { Token } from '../types.js' export interface TokenArgs { config: CliConfig component?: string format: OutputFormat + nutuiVersion?: string } export function runToken(args: TokenArgs): void { const { config } = args - const meta = loadMeta(config.dataDir) + const { meta, versionInfo } = resolveContext(config, args.nutuiVersion) + const vc = { + header: versionHeaderText(config, versionInfo), + meta: versionMeta(versionInfo), + } // 无参:全局 token。 if (!args.component) { - output(args.format, { scope: 'global', tokens: meta.globalTokens }, () => - renderTokenText( - `${config.libLabel}全局 Design Token(${meta.globalTokens.length} 个)`, - meta.globalTokens - ) + output( + args.format, + { scope: 'global', tokens: meta.globalTokens }, + () => + renderTokenText( + `${config.libLabel}全局 Design Token(${meta.globalTokens.length} 个)`, + meta.globalTokens + ), + vc ) return } @@ -29,15 +43,20 @@ export function runToken(args: TokenArgs): void { // 有参:组件 token。 const comp = resolveOrExit(config, meta, args.component) const tokens = comp.tokens ?? [] - output(args.format, { scope: comp.id, name: comp.name, tokens }, () => { - if (!tokens.length) { - return `${comp.name} ${comp.cName} 无专属 Design Token(可运行 ${config.binName} token 查看全局 token)。` - } - return renderTokenText( - `${comp.name} ${comp.cName} 的 Design Token(${tokens.length} 个)`, - tokens - ) - }) + output( + args.format, + { scope: comp.id, name: comp.name, tokens }, + () => { + if (!tokens.length) { + return `${comp.name} ${comp.cName} 无专属 Design Token(可运行 ${config.binName} token 查看全局 token)。` + } + return renderTokenText( + `${comp.name} ${comp.cName} 的 Design Token(${tokens.length} 个)`, + tokens + ) + }, + vc + ) } function renderTokenText(title: string, tokens: Token[]): string { diff --git a/packages/nutui-react-cli-core/src/config.ts b/packages/nutui-react-cli-core/src/config.ts index 690f253177..8b58d91e65 100644 --- a/packages/nutui-react-cli-core/src/config.ts +++ b/packages/nutui-react-cli-core/src/config.ts @@ -6,6 +6,9 @@ export interface CliConfig { binName: string // npm 包名,用于 MCP TTY 提示与文档链接。'@nutui/nutui-react-cli' | '@nutui/nutui-react-taro-cli' cliPkgName: string + // 被查询的 NutUI 运行时 npm 包名,用于版本自动检测(读项目 node_modules / package.json 依赖)。 + // '@nutui/nutui-react' | '@nutui/nutui-react-taro' + npmPackageName: string platform: 'h5' | 'taro' // list 命令头部标签。'NutUI React(H5)' | 'NutUI React(Taro 多端)' libLabel: string diff --git a/packages/nutui-react-cli-core/src/data.ts b/packages/nutui-react-cli-core/src/data.ts index 5270e3b56c..f85b153ea6 100644 --- a/packages/nutui-react-cli-core/src/data.ts +++ b/packages/nutui-react-cli-core/src/data.ts @@ -1,21 +1,111 @@ -// 数据查询原语。DATA_DIR 由各叶子包通过 CliConfig.dataDir 注入(各包用 import.meta.url -// 相对定位自己的随包 data/ 快照),core 不再自行用 import.meta.url 定位。 +// 数据查询原语。dataDir 由各叶子包通过 CliConfig.dataDir 注入(各包用 import.meta.url +// 相对定位自己的随包 data/ 快照根,其下为 versions.json + v{tag}/ 多版本目录)。 +// +// 多版本:dataDir 不再直接含 meta.json,而是 dataDir/versions.json + dataDir/v{tag}/meta.json。 +// 命令层先 detectVersion 得到目标版本,再 resolveSnapshotDir 路由到某个 v{tag}/ 目录(snapshotDir), +// 之后所有读取都基于该 snapshotDir。 import fs from 'node:fs' import path from 'node:path' -import type { Component, Lang, Meta } from './types.js' +import type { Component, Lang, Meta, VersionsIndex } from './types.js' -let cachedMeta: Meta | null = null +// 按快照目录缓存 meta,避免重复读盘。快照目录内容在进程生命周期内不变。 +const metaCache = new Map() -export function loadMeta(dataDir: string): Meta { - if (cachedMeta) return cachedMeta - const metaPath = path.join(dataDir, 'meta.json') +export function loadMetaByDir(snapshotDir: string): Meta { + const cached = metaCache.get(snapshotDir) + if (cached) return cached + const metaPath = path.join(snapshotDir, 'meta.json') if (!fs.existsSync(metaPath)) { throw new Error( `未找到打包数据 ${metaPath}。若为源码开发,请先运行 pnpm run build(或 pnpm run prepare-data)。` ) } - cachedMeta = JSON.parse(fs.readFileSync(metaPath, 'utf-8')) as Meta - return cachedMeta + const meta = JSON.parse(fs.readFileSync(metaPath, 'utf-8')) as Meta + metaCache.set(snapshotDir, meta) + return meta +} + +// versions.json 缓存(按 dataDir)。 +const versionsCache = new Map() + +export function loadVersionsIndex(dataDir: string): VersionsIndex { + const cached = versionsCache.get(dataDir) + if (cached) return cached + const p = path.join(dataDir, 'versions.json') + if (!fs.existsSync(p)) { + throw new Error( + `未找到 ${p}。若为源码开发,请先运行 pnpm run prepare-data 生成多版本快照。` + ) + } + const idx = JSON.parse(fs.readFileSync(p, 'utf-8')) as VersionsIndex + versionsCache.set(dataDir, idx) + return idx +} + +// 把不带/带 v 前缀的 tag 串统一成快照目录名 v{tag}。 +function snapshotDirFor(dataDir: string, tag: string): string { + const clean = tag.replace(/^v/, '') + return path.join(dataDir, `v${clean}`) +} + +/** + * 把一个完整版本号路由到最接近的历史快照目录(复刻 antd loader 的解析顺序)。 + * 1. 精确 minor 命中:versions.majors[vX].minors["X.Y"] → data/v{tag}/ + * 2. 最近不超过请求 minor 的旧 minor + * 3. 回退该 major 的 latest 主快照 + * 4. major 不存在 → 回退 defaultMajor 的 latest,stderr 提示 + * 返回存在的 snapshotDir 绝对路径。 + */ +export function resolveSnapshotDir(dataDir: string, version: string): string { + const idx = loadVersionsIndex(dataDir) + const parts = version.split('.') + const major = `v${parts[0]}` + + const majorIndex = idx.majors[major] + + // major 不存在 → 回退 defaultMajor + if (!majorIndex) { + const fb = idx.majors[idx.defaultMajor] + process.stderr.write( + `[nutui-cli] 未找到 NutUI ${major} 的离线数据,回退到默认版本 ${idx.defaultMajor}(${fb?.latest ?? '未知'})。\n` + ) + return snapshotDirFor(dataDir, fb.latest) + } + + const tryDir = (tag: string): string | null => { + const dir = snapshotDirFor(dataDir, tag) + return fs.existsSync(path.join(dir, 'meta.json')) ? dir : null + } + + // 不是可识别的 major.minor.patch → 直接用该 major latest + if (parts[1] === undefined || parts[1] === '') { + return tryDir(majorIndex.latest) ?? snapshotDirFor(dataDir, majorIndex.latest) + } + + const minorKey = `${parts[0]}.${parts[1]}` + + // 1. 精确 minor 命中 + if (majorIndex.minors[minorKey]) { + const dir = tryDir(majorIndex.minors[minorKey]) + if (dir) return dir + } + + // 2. 最近不超过请求 minor 的旧 minor + const requestedMinor = parseInt(parts[1], 10) + const availableMinors = Object.keys(majorIndex.minors) + .filter((k) => k.startsWith(`${parts[0]}.`)) + .sort((a, b) => parseInt(a.split('.')[1], 10) - parseInt(b.split('.')[1], 10)) + let bestMinorKey: string | undefined + for (const m of availableMinors) { + if (parseInt(m.split('.')[1], 10) <= requestedMinor) bestMinorKey = m + } + if (bestMinorKey && majorIndex.minors[bestMinorKey]) { + const dir = tryDir(majorIndex.minors[bestMinorKey]) + if (dir) return dir + } + + // 3. 回退该 major latest + return tryDir(majorIndex.latest) ?? snapshotDirFor(dataDir, majorIndex.latest) } // 组件名大小写不敏感解析:Button / button / BUTTON 均可。命中返回组件,否则 null。 @@ -62,17 +152,17 @@ export function suggestComponents( // 读取组件某语言的文档原文。快照期 prepare-data 已按 .md 落盘(zh.md / en.md), // 故此处直接以 lang 寻址,无需 key 映射。缺失(如 Taro 无英文)返回 null。 export function readDoc( - dataDir: string, + snapshotDir: string, component: Component, lang: Lang ): string | null { - const file = path.join(dataDir, 'docs', component.id, `${lang}.md`) + const file = path.join(snapshotDir, 'docs', component.id, `${lang}.md`) return fs.existsSync(file) ? fs.readFileSync(file, 'utf-8') : null } // 列出组件的全部 demo 文件名(不含扩展名,如 demo1),按序号排序。 -export function listDemos(dataDir: string, component: Component): string[] { - const dir = path.join(dataDir, 'demos', component.id) +export function listDemos(snapshotDir: string, component: Component): string[] { + const dir = path.join(snapshotDir, 'demos', component.id) if (!fs.existsSync(dir)) return [] return fs .readdirSync(dir) @@ -87,7 +177,7 @@ export function listDemos(dataDir: string, component: Component): string[] { // 读取组件某个 demo 的源码。name 形如 demo1(也容忍带 .tsx)。缺失返回 null。 export function readDemo( - dataDir: string, + snapshotDir: string, component: Component, name: string ): string | null { @@ -96,6 +186,17 @@ export function readDemo( return null } const base = name.endsWith('.tsx') ? name : `${name}.tsx` - const file = path.join(dataDir, 'demos', component.id, base) + const file = path.join(snapshotDir, 'demos', component.id, base) + return fs.existsSync(file) ? fs.readFileSync(file, 'utf-8') : null +} + +// 读取某个快照目录下的迁移文档原文(prepare-data 从对应 tag worktree 抽取而来)。 +// fromMajor 形如 3(表示"从 v3 升级到当前快照版本");文档路径为 migrations/from-v{n}.md。 +// 该文档只在含迁移说明的版本(如 v4)快照里存在,缺失返回 null。 +export function readMigrationDoc( + snapshotDir: string, + fromMajor: number +): string | null { + const file = path.join(snapshotDir, 'migrations', `from-v${fromMajor}.md`) return fs.existsSync(file) ? fs.readFileSync(file, 'utf-8') : null } diff --git a/packages/nutui-react-cli-core/src/error.ts b/packages/nutui-react-cli-core/src/error.ts index 9b2df21069..6b0820098f 100644 --- a/packages/nutui-react-cli-core/src/error.ts +++ b/packages/nutui-react-cli-core/src/error.ts @@ -4,6 +4,8 @@ export const ErrorCodes = { COMPONENT_NOT_FOUND: 'COMPONENT_NOT_FOUND', DOC_NOT_FOUND: 'DOC_NOT_FOUND', DEMO_NOT_FOUND: 'DEMO_NOT_FOUND', + VERSION_NOT_FOUND: 'VERSION_NOT_FOUND', + INVALID_ARGUMENT: 'INVALID_ARGUMENT', UNKNOWN_TOOL: 'UNKNOWN_TOOL', } as const diff --git a/packages/nutui-react-cli-core/src/format.ts b/packages/nutui-react-cli-core/src/format.ts index a191e5038b..3680a44025 100644 --- a/packages/nutui-react-cli-core/src/format.ts +++ b/packages/nutui-react-cli-core/src/format.ts @@ -33,11 +33,30 @@ function pad(s: string, width: number): string { } // 统一的「按格式输出」入口:json 走 printJson,text 走回调产出的文本。 +// 可选 versionCtx:json 时在顶层并入 `_meta`,text 时在正文前加一行版本信息头, +// 让用户/Agent 明确当前查询命中的是哪个大版本、来源为何。 export function output( format: OutputFormat, jsonData: unknown, - toText: () => string + toText: () => string, + versionCtx?: { header: string; meta: Record } ): void { - if (format === 'json') printJson(jsonData) - else printText(toText()) + if (format === 'json') { + if ( + versionCtx && + jsonData && + typeof jsonData === 'object' && + !Array.isArray(jsonData) + ) { + printJson({ _meta: versionCtx.meta, ...(jsonData as object) }) + } else if (versionCtx) { + printJson({ _meta: versionCtx.meta, data: jsonData }) + } else { + printJson(jsonData) + } + } else { + printText( + versionCtx && versionCtx.header ? `${versionCtx.header}\n\n${toText()}` : toText() + ) + } } diff --git a/packages/nutui-react-cli-core/src/mcp/tools.ts b/packages/nutui-react-cli-core/src/mcp/tools.ts index 4d18b74496..97e9a7ee59 100644 --- a/packages/nutui-react-cli-core/src/mcp/tools.ts +++ b/packages/nutui-react-cli-core/src/mcp/tools.ts @@ -1,14 +1,24 @@ -// MCP 工具定义与处理器。复用 data.ts 原语,与 CLI 命令共享同一份 meta 快照, +// MCP 工具定义与处理器。复用 data.ts 原语,与 CLI 命令共享同一套多版本快照路由, // 输出结构与各命令的 --format json 保持一致。工具名 = config.mcp.toolPrefix + 动词。 +// +// 多版本:每个工具入参新增可选 nutuiVersion(与 CLI 的 --nutui-version 对齐)。handler +// 内部走同一套 detectVersion → resolveSnapshotDir → loadMetaByDir,路由到对应快照。 import { listDemos, - loadMeta, + loadMetaByDir, + loadVersionsIndex, readDemo, readDoc, + readMigrationDoc, resolveComponent, + resolveSnapshotDir, suggestComponents, } from '../data.js' +import { detectVersion, type VersionInfo } from '../version.js' import { createError, ErrorCodes } from '../error.js' +import { parseMigrationDoc, filterSections } from '../commands/migrate-parse.js' +import { diffMeta } from '../commands/diff-compute.js' +import { scanProject } from '../utils/scan.js' import type { CliConfig } from '../config.js' import type { Component, Lang, Meta } from '../types.js' @@ -64,6 +74,19 @@ function langEnumDesc(config: CliConfig): string { return `文档语言,${parts.join(',')}` } +// 目标版本参数描述(各工具共用)。 +const NUTUI_VERSION_DESC = + '目标 NutUI 版本(如 3、3.1.0、4.0.0-beta.7);省略则从项目 node_modules / package.json 自动检测,检测不到则用默认大版本' + +function nutuiVersionProp() { + return { + nutuiVersion: { + type: 'string', + description: NUTUI_VERSION_DESC, + }, + } +} + export function buildToolDefinitions(config: CliConfig) { const p = config.mcp.toolPrefix return [ @@ -78,6 +101,7 @@ export function buildToolDefinitions(config: CliConfig) { description: '按分类英文名筛选(如 base / feedback / form),省略则列出全部', }, + ...nutuiVersionProp(), }, required: [] as string[], }, @@ -92,8 +116,9 @@ export function buildToolDefinitions(config: CliConfig) { properties: { component: { type: 'string', - description: '组件名(大小写不敏感,如 Button)', + description: '组件名(大小写不敏感,如 Button)', }, + ...nutuiVersionProp(), }, required: ['component'], }, @@ -107,13 +132,14 @@ export function buildToolDefinitions(config: CliConfig) { properties: { component: { type: 'string', - description: '组件名(大小写不敏感,如 Button)', + description: '组件名(大小写不敏感,如 Button)', }, lang: { type: 'string', enum: [...config.langs], description: langEnumDesc(config), }, + ...nutuiVersionProp(), }, required: ['component'], }, @@ -127,12 +153,13 @@ export function buildToolDefinitions(config: CliConfig) { properties: { component: { type: 'string', - description: '组件名(大小写不敏感,如 Button)', + description: '组件名(大小写不敏感,如 Button)', }, name: { type: 'string', description: '示例名(如 demo1);省略则列出全部示例', }, + ...nutuiVersionProp(), }, required: ['component'], }, @@ -149,18 +176,74 @@ export function buildToolDefinitions(config: CliConfig) { type: 'string', description: '组件名,返回组件级 token;省略则返回全局 token', }, + ...nutuiVersionProp(), }, required: [] as string[], }, annotations: { title: '查询 Design Token', ...TOOL_ANNOTATIONS }, }, + { + name: `${p}migrate`, + description: + '获取 NutUI 大版本迁移指南(默认 v3→v4)。component 只看某组件;applyDir 扫描项目目录,只返回项目用到组件的迁移步骤。数据源是随包抽取的官方迁移文档。', + inputSchema: { + type: 'object' as const, + properties: { + from: { type: 'string', description: '起始大版本(如 3),默认 3' }, + to: { type: 'string', description: '目标大版本(如 4),默认 from+1' }, + component: { + type: 'string', + description: '只返回该组件的迁移说明(大小写不敏感)', + }, + applyDir: { + type: 'string', + description: '扫描该目录,只返回项目实际用到组件的迁移步骤', + }, + }, + required: [] as string[], + }, + annotations: { title: '版本迁移指南', ...TOOL_ANNOTATIONS }, + }, + { + name: `${p}diff`, + description: + '跨版本 Props 差异比对:给定两个版本,返回各组件新增 / 移除 / 类型或默认值变更的 Props。', + inputSchema: { + type: 'object' as const, + properties: { + v1: { type: 'string', description: '起始版本(如 3、3.1.0)' }, + v2: { type: 'string', description: '目标版本(如 4、4.0.0-beta.7)' }, + component: { + type: 'string', + description: '只比对该组件(大小写不敏感);省略则比对全部', + }, + }, + required: ['v1', 'v2'], + }, + annotations: { title: '跨版本 Props 差异', ...TOOL_ANNOTATIONS }, + }, ] } export function createToolHandler(config: CliConfig) { const p = config.mcp.toolPrefix return async (name: string, params: Record) => { - const meta = loadMeta(config.dataDir) + // 每次调用按入参 nutuiVersion 路由到对应快照(省略则自动检测)。 + const versionsIndex = loadVersionsIndex(config.dataDir) + const versionInfo: VersionInfo = detectVersion({ + flag: params.nutuiVersion as string | undefined, + cwd: process.cwd(), + npmPackageName: config.npmPackageName, + versionsIndex, + }) + const snapshotDir = resolveSnapshotDir(config.dataDir, versionInfo.version) + const meta = loadMetaByDir(snapshotDir) + const _meta = { + version: versionInfo.version, + major: versionInfo.major, + source: versionInfo.source, + } + // 去前缀得到动词,兼容两端不同前缀。 const verb = name.startsWith(p) ? name.slice(p.length) : name @@ -186,7 +269,7 @@ export function createToolHandler(config: CliConfig) { version: c.version, })), })) - return toMcpResult({ libVersion: meta.libVersion, categories: data }) + return toMcpResult({ _meta, libVersion: meta.libVersion, categories: data }) } case 'info': { @@ -196,6 +279,7 @@ export function createToolHandler(config: CliConfig) { (t) => t.kind === 'props' ) return toMcpResult({ + _meta, name: comp.name, cName: comp.cName, version: comp.version, @@ -211,7 +295,7 @@ export function createToolHandler(config: CliConfig) { const comp = resolve(config, meta, params.component as string) if (isError(comp)) return toMcpResult(comp) const lang = (params.lang as Lang) ?? config.defaultLang - const content = readDoc(config.dataDir, comp, lang) + const content = readDoc(snapshotDir, comp, lang) if (content === null) { const langName = config.langLabel[lang] ?? lang return toMcpResult( @@ -221,18 +305,18 @@ export function createToolHandler(config: CliConfig) { ) ) } - return toMcpResult({ name: comp.name, lang, doc: content }) + return toMcpResult({ _meta, name: comp.name, lang, doc: content }) } case 'demo': { const comp = resolve(config, meta, params.component as string) if (isError(comp)) return toMcpResult(comp) - const demos = listDemos(config.dataDir, comp) + const demos = listDemos(snapshotDir, comp) const demoName = params.name as string | undefined if (!demoName) { - return toMcpResult({ component: comp.name, demos }) + return toMcpResult({ _meta, component: comp.name, demos }) } - const code = readDemo(config.dataDir, comp, demoName) + const code = readDemo(snapshotDir, comp, demoName) if (code === null) { return toMcpResult( createError( @@ -244,23 +328,132 @@ export function createToolHandler(config: CliConfig) { ) ) } - return toMcpResult({ component: comp.name, demo: demoName, code }) + return toMcpResult({ _meta, component: comp.name, demo: demoName, code }) } case 'token': { const query = params.component as string | undefined if (!query) { - return toMcpResult({ scope: 'global', tokens: meta.globalTokens }) + return toMcpResult({ _meta, scope: 'global', tokens: meta.globalTokens }) } const comp = resolve(config, meta, query) if (isError(comp)) return toMcpResult(comp) return toMcpResult({ + _meta, scope: comp.id, component: comp.name, tokens: comp.tokens ?? [], }) } + case 'migrate': { + // 迁移跨两个大版本,自己解析,不用预加载的单快照 meta。 + const toMajorNum = (v: unknown): number | null => { + if (!v) return null + const m = String(v).replace(/^v/, '').match(/^(\d+)/) + return m ? Number(m[1]) : null + } + const fromMajor = toMajorNum(params.from) ?? 3 + const toMajor = toMajorNum(params.to) ?? fromMajor + 1 + if (toMajor <= fromMajor) { + return toMcpResult( + createError( + ErrorCodes.INVALID_ARGUMENT, + `迁移方向无效:from=v${fromMajor} to=v${toMajor}。请指定从低到高的大版本。` + ) + ) + } + const toKey = `v${toMajor}` + if (!versionsIndex.majors[toKey]) { + return toMcpResult( + createError( + ErrorCodes.VERSION_NOT_FOUND, + `未找到 NutUI v${toMajor} 的离线数据,无法提供 v${fromMajor}→v${toMajor} 迁移指南。` + ) + ) + } + const toDir = resolveSnapshotDir(config.dataDir, String(toMajor)) + const md = readMigrationDoc(toDir, fromMajor) + if (md === null) { + return toMcpResult( + createError( + ErrorCodes.DOC_NOT_FOUND, + `暂无 v${fromMajor}→v${toMajor} 的迁移文档数据。` + ) + ) + } + const parsed = parseMigrationDoc(md) + const applyDir = params.applyDir as string | undefined + const compName = params.component as string | undefined + let sections = parsed.sections + let scanInfo: Record = {} + if (applyDir) { + const scan = scanProject(applyDir) + sections = filterSections(parsed.sections, scan.components) + const covered = new Set(sections.map((s) => s.component.toLowerCase())) + scanInfo = { + appliedTo: applyDir, + scannedFiles: scan.scannedFileCount, + projectComponents: scan.components, + matchedComponents: sections.map((s) => s.component), + componentsWithoutBreakingChanges: scan.components.filter( + (c) => !covered.has(c.toLowerCase()) + ), + } + } else if (compName) { + sections = filterSections(parsed.sections, [compName]) + } + return toMcpResult({ + _meta: { from: `v${fromMajor}`, to: `v${toMajor}`, libVersion: versionsIndex.majors[toKey].latest }, + title: parsed.title, + intro: applyDir || compName ? undefined : parsed.intro, + coveredComponents: parsed.sections.map((s) => s.component), + steps: sections.map((s) => ({ + component: s.component, + category: s.category, + guide: s.raw, + })), + ...scanInfo, + }) + } + + case 'diff': { + const v1 = params.v1 as string + const v2 = params.v2 as string + if (!v1 || !v2) { + return toMcpResult( + createError(ErrorCodes.INVALID_ARGUMENT, 'diff 需要 v1 与 v2 两个版本参数。') + ) + } + const metaA = loadMetaByDir(resolveSnapshotDir(config.dataDir, v1)) + const metaB = loadMetaByDir(resolveSnapshotDir(config.dataDir, v2)) + const compName = params.component as string | undefined + let componentId: string | undefined + if (compName) { + const comp = resolveComponent(metaB, compName) ?? resolveComponent(metaA, compName) + if (!comp) { + const sug = suggestComponents(metaB, compName) + return toMcpResult( + createError( + ErrorCodes.COMPONENT_NOT_FOUND, + `未找到组件「${compName}」。`, + sug.length ? `你是否想找:${sug.join(' / ')}?` : undefined + ) + ) + } + componentId = comp.id + } + const diffs = diffMeta(metaA, metaB, componentId) + return toMcpResult({ + _meta: { v1: metaA.libVersion, v2: metaB.libVersion }, + v1: metaA.libVersion, + v2: metaB.libVersion, + component: compName ?? null, + changedComponentCount: diffs.length, + components: diffs, + }) + } + default: return toMcpResult( createError(ErrorCodes.UNKNOWN_TOOL, `未知工具:${name}`) diff --git a/packages/nutui-react-cli-core/src/types.ts b/packages/nutui-react-cli-core/src/types.ts index f4e86a098f..85837f5f63 100644 --- a/packages/nutui-react-cli-core/src/types.ts +++ b/packages/nutui-react-cli-core/src/types.ts @@ -73,3 +73,20 @@ export interface Meta { export type Lang = string export type OutputFormat = 'text' | 'json' + +// data/versions.json 的结构:把 major.minor 映射到具体 tag,供运行时把请求版本 +// 路由到最接近的历史快照目录。由 scripts/sync.mjs 生成。 +export interface MajorIndex { + // 该 major 的 latest tag(不带 v 前缀,如 '3.1.0' / '4.0.0-beta.7') + latest: string + // 是否有稳定版(v4 目前全 beta → false,命中时不打「预发布」warning) + stable: boolean + // major.minor → tag(不带 v 前缀),如 { '3.0': '3.0.20', '3.1': '3.1.0' } + minors: Record +} + +export interface VersionsIndex { + // 兜底 major,如 'v4' + defaultMajor: string + majors: Record +} diff --git a/packages/nutui-react-cli-core/src/utils/scan.ts b/packages/nutui-react-cli-core/src/utils/scan.ts new file mode 100644 index 0000000000..52607b35b2 --- /dev/null +++ b/packages/nutui-react-cli-core/src/utils/scan.ts @@ -0,0 +1,114 @@ +// 项目扫描:递归遍历源码目录,找出从 @nutui/nutui-react(-taro) 具名 import 的组件。 +// 供 migrate --apply 用——只对项目实际用到的组件输出迁移步骤。纯 node:fs + 正则,无第三方依赖。 +import fs from 'node:fs' +import path from 'node:path' + +export interface ScanResult { + // 项目中出现过的 NutUI 组件名(保留源码里的原始拼写,去重) + components: string[] + // 扫描到的、含 NutUI import 的文件绝对路径 + files: string[] + // 实际遍历的源码文件总数(用于"扫描了 N 个文件"提示) + scannedFileCount: number +} + +const SOURCE_EXT = new Set(['.tsx', '.ts', '.jsx', '.js', '.mjs', '.cjs']) +const SKIP_DIRS = new Set([ + 'node_modules', + '.git', + 'dist', + 'build', + '.next', + '.turbo', + 'coverage', +]) + +// 匹配 import { ... } from '@nutui/nutui-react' | '@nutui/nutui-react-taro' +// 也匹配 icons 包(@nutui/icons-react(-taro)),图标名也算"用到的组件"以便迁移提示覆盖图标变更。 +// [\s\S]*? 跨行匹配具名列表;末尾包名用锚点确保不误伤子路径 import。 +const IMPORT_RE = + /import\s+(?:type\s+)?\{([\s\S]*?)\}\s*from\s*['"]@nutui\/(?:nutui-react|icons-react)(?:-taro)?['"]/g + +// 从具名列表字符串(可能含换行、注释、as 别名)解析出组件名集合。 +function parseNamedImports(inner: string): string[] { + return inner + .split(',') + .map((s) => + s + // 去掉行内 / 块注释残片 + .replace(/\/\/.*$/gm, '') + .replace(/\/\*[\s\S]*?\*\//g, '') + .trim() + ) + .filter(Boolean) + // `Button as Btn` → 取原名 Button;`type Foo` → 去掉 type 前缀 + .map((s) => s.replace(/^type\s+/, '').split(/\s+as\s+/)[0].trim()) + .filter((s) => /^[A-Za-z][A-Za-z0-9_]*$/.test(s)) +} + +// 从单个文件内容里抽取 NutUI 组件名。无命中返回空数组。 +export function extractComponents(content: string): string[] { + const found = new Set() + for (const m of content.matchAll(IMPORT_RE)) { + for (const name of parseNamedImports(m[1])) found.add(name) + } + return [...found] +} + +function walk(dir: string, onFile: (file: string) => void): void { + let entries: fs.Dirent[] + try { + entries = fs.readdirSync(dir, { withFileTypes: true }) + } catch { + return + } + for (const entry of entries) { + if (entry.name.startsWith('.') && entry.name !== '.') { + // 跳过隐藏目录/文件(.git 等),但允许显式传入的 '.' 起点 + if (entry.isDirectory() && SKIP_DIRS.has(entry.name)) continue + if (entry.isDirectory()) continue + } + const full = path.join(dir, entry.name) + if (entry.isDirectory()) { + if (SKIP_DIRS.has(entry.name)) continue + walk(full, onFile) + } else if (entry.isFile() && SOURCE_EXT.has(path.extname(entry.name))) { + onFile(full) + } + } +} + +// 扫描目录,聚合项目用到的 NutUI 组件。dir 可为文件或目录。 +export function scanProject(dir: string): ScanResult { + const components = new Set() + const files: string[] = [] + let scannedFileCount = 0 + + const handle = (file: string) => { + scannedFileCount++ + let content: string + try { + content = fs.readFileSync(file, 'utf-8') + } catch { + return + } + const names = extractComponents(content) + if (names.length) { + files.push(file) + for (const n of names) components.add(n) + } + } + + const stat = fs.existsSync(dir) ? fs.statSync(dir) : null + if (stat?.isFile()) { + handle(dir) + } else { + walk(dir, handle) + } + + return { + components: [...components].sort(), + files: files.sort(), + scannedFileCount, + } +} diff --git a/packages/nutui-react-cli-core/src/version.ts b/packages/nutui-react-cli-core/src/version.ts new file mode 100644 index 0000000000..ec9157a0ae --- /dev/null +++ b/packages/nutui-react-cli-core/src/version.ts @@ -0,0 +1,103 @@ +// NutUI 目标版本检测:复刻 antd CLI 的四层回落,把 npm 包名参数化,供 H5 / Taro 共用。 +// 解析顺序:--nutui-version → node_modules/ → 项目 package.json 依赖声明 → defaultMajor 兜底。 +import fs from 'node:fs' +import path from 'node:path' +import semver from 'semver' +import type { VersionsIndex } from './types.js' + +export interface VersionInfo { + version: string // 解析出的完整版本号,如 '3.1.0' / '4.0.0-beta.7' + major: string // 'v3' / 'v4' + source: 'flag' | 'node_modules' | 'package.json' | 'fallback' +} + +export interface DetectVersionOptions { + flag?: string + cwd?: string + npmPackageName: string // '@nutui/nutui-react' | '@nutui/nutui-react-taro' + versionsIndex: VersionsIndex +} + +function toMajor(version: string): string { + return `v${version.split('.')[0]}` +} + +function readJson(file: string): T | null { + try { + return JSON.parse(fs.readFileSync(file, 'utf-8')) as T + } catch { + return null + } +} + +// 拿到完整版本后,若其 major 不在离线数据里,回退 defaultMajor 并提示。 +function withKnownMajor( + version: string, + source: VersionInfo['source'], + idx: VersionsIndex +): VersionInfo { + const major = toMajor(version) + if (idx.majors[major]) { + return { version, major, source } + } + const fbMajor = idx.defaultMajor + const fbVersion = idx.majors[fbMajor]?.latest ?? version + process.stderr.write( + `[nutui-cli] 未找到 NutUI ${major} 的离线数据,回退到默认版本 ${fbMajor}(${fbVersion})。\n` + ) + return { version: fbVersion, major: fbMajor, source } +} + +export function detectVersion(opts: DetectVersionOptions): VersionInfo { + const cwd = opts.cwd ?? process.cwd() + const idx = opts.versionsIndex + + // 1. --nutui-version flag:先 parse 保留预发布串,再 coerce 兼容 "3"、"^3.1.0" 等。 + if (opts.flag) { + const parsed = + semver.parse(opts.flag) ?? + semver.coerce(opts.flag, { includePrerelease: true }) + if (parsed) { + return withKnownMajor(parsed.version, 'flag', idx) + } + process.stderr.write( + `[nutui-cli] Warning: --nutui-version "${opts.flag}" 不是有效的 semver,改用自动检测。\n` + ) + } + + // 2. node_modules//package.json + const nmPath = path.join(cwd, 'node_modules', opts.npmPackageName, 'package.json') + if (fs.existsSync(nmPath)) { + const pkg = readJson<{ version?: string }>(nmPath) + if (pkg?.version) { + return withKnownMajor(pkg.version, 'node_modules', idx) + } + } + + // 3. 项目 package.json 的 deps / devDeps / peerDeps 声明 + const pkgPath = path.join(cwd, 'package.json') + if (fs.existsSync(pkgPath)) { + const pkg = readJson<{ + dependencies?: Record + devDependencies?: Record + peerDependencies?: Record + }>(pkgPath) + const dep = + pkg?.dependencies?.[opts.npmPackageName] ?? + pkg?.devDependencies?.[opts.npmPackageName] ?? + pkg?.peerDependencies?.[opts.npmPackageName] + if (dep) { + const parsed = + semver.parse(dep) ?? semver.coerce(dep, { includePrerelease: true }) + if (parsed) { + return withKnownMajor(parsed.version, 'package.json', idx) + } + // 非 semver(如 'workspace:*' / '*')→ 落到 fallback + } + } + + // 4. Fallback:defaultMajor 的 latest + const fbMajor = idx.defaultMajor + const fbVersion = idx.majors[fbMajor]?.latest ?? '0.0.0' + return { version: fbVersion, major: fbMajor, source: 'fallback' } +} diff --git a/packages/nutui-react-cli-core/vitest.config.ts b/packages/nutui-react-cli-core/vitest.config.ts new file mode 100644 index 0000000000..d2d969041c --- /dev/null +++ b/packages/nutui-react-cli-core/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + include: ['src/**/*.test.ts'], + environment: 'node', + }, +}) diff --git a/packages/nutui-react-cli/CHANGELOG.md b/packages/nutui-react-cli/CHANGELOG.md index c47b57e4cc..be1571930e 100644 --- a/packages/nutui-react-cli/CHANGELOG.md +++ b/packages/nutui-react-cli/CHANGELOG.md @@ -2,6 +2,13 @@ 本包版本号自 `4.0.0` 起与组件包 `@nutui/nutui-react` 对齐,便于未来支持多版本查询。 +## v4.1.0 + +`2026-09-04` + +- feat: 新增多版本知识查询能力 +- feat: 新增 v3 -> v4 迁移的能力 + ## v4.0.0 `2026-08-21` diff --git a/packages/nutui-react-cli/README.md b/packages/nutui-react-cli/README.md index 4ae7ae62a7..e1af179f9b 100644 --- a/packages/nutui-react-cli/README.md +++ b/packages/nutui-react-cli/README.md @@ -33,9 +33,16 @@ nutui-react info Button --format json | `nutui-react doc [--lang zh\|en]` | 查看组件完整文档,默认中文 | | `nutui-react demo [name]` | 省略 `name` 列出全部示例;指定 `name`(如 `demo1`)输出源码 | | `nutui-react token [Component]` | 查看 Design Token;省略组件名则列出全局 token | +| `nutui-react migrate [from] [to]` | 输出 vN→vM 迁移指南(默认 `3 4`);`--component ` 看单组件,`--apply ` 扫描项目生成迁移提示 | +| `nutui-react diff [Component]` | 跨版本 Props 差异对比(新增 / 移除 / 类型或默认值变更) | | `nutui-react mcp` | 启动本地 MCP 服务(stdio),供 Claude Code / Cursor / VS Code / Codex 等 IDE 调用 | -全局选项:`--format, -f `(默认 `text`)、`--help, -h`、`--version, -v`。 +全局选项: + +- `--format, -f `(默认 `text`) +- `--nutui-version, --nv `(目标 NutUI 版本,见下) +- `--help, -h` +- `--version, -v`(CLI 自身版本)。 示例: @@ -46,13 +53,55 @@ nutui-react doc Button --lang en nutui-react demo Button # 列出示例 nutui-react demo Button demo1 # 查看某个示例源码 nutui-react token Button +nutui-react migrate 3 4 # v3→v4 迁移指南 +nutui-react migrate 3 4 --apply ./src --format json # 扫描项目,生成给 Agent 的迁移提示 +nutui-react diff 3.1.0 4.0.0-beta.7 Empty # 跨版本 Props 差异 +``` + +## 多版本 + +CLI 随包内置多个 NutUI 大版本的离线快照(当前:`v3.0.20`、`v3.1.0`、`v4.0.0-beta.7`),可对同一台机器上不同 NutUI 版本给出对应的组件知识。 + +用 `--nutui-version`(别名 `--nv`)指定目标版本,支持 `3`、`3.1.0`、`4.0.0-beta.7` 等写法: + +```bash +nutui-react --nv 3.1.0 info Button # 查 3.1.0 的 Button Props +nutui-react --nv 3 list # 查 v3 最新快照 +nutui-react --nv 4.0.0-beta.7 doc Cell +``` + +按以下顺序**自动检测**目标版本: + +1. `--nutui-version ` 显式指定; +2. 项目 `node_modules/@nutui/nutui-react/package.json` 的实际安装版本; +3. 项目 `package.json` 的 `dependencies` / `devDependencies` / `peerDependencies` 声明(兼容 `^3.1.0`、`~3.1.0` 等); +4. 兜底到默认大版本(`v4`)的 latest。 + +版本路由按 `major.minor` 粒度:请求 `3.0.5` 会落到该 minor 系列的最高 patch 快照 `v3.0.20`;请求的 minor 若高于已有,回退到该 major 最近的旧 minor。每次查询的输出都会标明实际命中的版本与来源。 + +> 在项目里能自动推断版本时,优先不显式传 `--nutui-version`,让 CLI 自行检测更省心。 + +## 版本迁移(v3 → v4) + +两个命令支撑从 v3 升级到 v4,数据均随包离线分发: + +- `nutui-react migrate 3 4` —— 输出**官方迁移文档**。`--component ` 只看某组件;`--apply ` 扫描项目、只输出实际用到组件的迁移步骤,并生成一段给 Code Agent 的定向提示。 +- `nutui-react diff [Component]` —— 实时对比两个版本快照的 Props,列出新增 / 移除 / 类型或默认值变更(精确 `旧→新`)。 + +二者**互补**:`migrate` 讲"为什么改、怎么改"(含样式 / CSS 类名 / Design Token),`diff` 给"API 层精确差了什么"。配套的 `nutui-react-v3-to-v4` Skill 会编排整个升级流程(见下)。 + +```bash +nutui-react migrate 3 4 --format json # 全量迁移清单 +nutui-react migrate 3 4 --component Empty # 只看 Empty +nutui-react migrate 3 4 --apply ./src --format json # 扫描项目 + 生成 Agent 提示 +nutui-react diff 3 4 Empty --format json # Empty 的 Props 差异 ``` ## MCP(IDE 集成) CLI 是「Agent 主动敲命令」,MCP 则把同一份能力注册成 IDE 原生工具,让 Claude Code / Cursor / VS Code / Codex 在对话中**按需自动调用**,无需拼命令行字符串。二者复用同一份离线 meta 快照,等于给同一个知识库套了两种调用协议。 -`nutui-react mcp` 启动一个 stdio MCP 服务,暴露 5 个只读工具与 2 个提示词: +`nutui-react mcp` 启动一个 stdio MCP 服务,暴露 7 个只读工具与 2 个提示词: | 工具 | 说明 | | --- | --- | @@ -61,6 +110,8 @@ CLI 是「Agent 主动敲命令」,MCP 则把同一份能力注册成 IDE 原 | `nutui_doc` | 组件完整文档(`lang: zh\|en`) | | `nutui_demo` | H5 示例列表 / 源码 | | `nutui_token` | Design Token(全局 / 组件级) | +| `nutui_migrate` | vN→vM 迁移指南(可按组件 / 扫描目录) | +| `nutui_diff` | 跨版本 Props 差异对比 | | 提示词 | 说明 | | --- | --- | @@ -112,6 +163,14 @@ npx skills add jdf2e/nutui-react --skill nutui-react npx skills add jdf2e/nutui-react --skill nutui-react-to-taro ``` +### 升级 Skill:v3 → v4 + +本包还内置一份 [升级 Skill](./skills/nutui-react-v3-to-v4/SKILL.md),指导 Agent 把项目从 NutUI React v3 升级到 v4。它编排「依赖升级 → `migrate --apply` 扫描项目 → 逐组件读 `migrate` + `diff` 改写 → 构建验证」的完整流程,并把 v3→v4 的关键事实讲清楚:破坏性变更集中在**样式 / CSS 类名 / Design Token / 枚举值 / 默认值**(如 Empty 的 `size`/`status`、Popover 的 `theme`、Toast 的 `duration`)。 + +```bash +npx skills add jdf2e/nutui-react --skill nutui-react-v3-to-v4 +``` + ## 本地开发 ```bash diff --git a/packages/nutui-react-cli/docs/mcp.md b/packages/nutui-react-cli/docs/mcp.md index 5cf22f76d9..d1abf3aa6d 100644 --- a/packages/nutui-react-cli/docs/mcp.md +++ b/packages/nutui-react-cli/docs/mcp.md @@ -1,12 +1,10 @@ # NutUI Knowledge -通过 Model Context Protocol (MCP) 教 AI 工具中使用 NutUI-React。 +通过 Model Context Protocol (MCP) 教 AI 工具使用 NutUI-React。 ## 启动 MCP 服务 -`nutui-react mcp` 启动一个通过 stdio 通信的本地 MCP 服务,暴露 5 个只读工具与 2 个提示词。该命令不应在终端直接裸跑,而应在 AI 工具中配置(见下方)。 - -### 工具 +`nutui-react mcp` 启动一个通过 stdio 通信的本地 MCP 服务,暴露 7 个只读工具与 2 个提示词。该命令不应在终端直接裸跑,而应在 AI 工具中配置(见下方)。 | 工具 | 说明 | | --- | --- | @@ -15,9 +13,16 @@ | `nutui_doc` | 获取组件完整文档(`lang: zh\|en`) | | `nutui_demo` | 获取组件 H5 示例列表 / 源码 | | `nutui_token` | 查询 Design Token(全局 / 组件级) | +| `nutui_migrate` | 获取大版本迁移指南;可按组件筛选或扫描项目目录 | +| `nutui_diff` | 对比两个版本快照的 Props 差异 | 所有工具均为只读、无副作用、不访问外部网络。 +前 5 个组件知识工具都接受可选入参 `nutuiVersion`(如 `3.1.0`、`4.0.0-beta.7`),用于指定目标 NutUI 大版本;省略时自动检测项目所用版本,检测不到则回退默认大版本。 + +- `nutui_migrate` 接受可选的 `from`、`to`、`component` 和 `applyDir` 参数; +- `applyDir` 只扫描目录并返回迁移步骤,不修改文件。`nutui_diff` 要求传入 `v1`、`v2`,也可用 `component` 限定单个组件。两者与 CLI 的 `migrate`、`diff` 命令返回同类结构化数据,可由 Agent 直接用于 v3 → v4 升级。 + ### 提示词 | 提示词 | 说明 | diff --git a/packages/nutui-react-cli/package.json b/packages/nutui-react-cli/package.json index 1e31f59e11..24de0bf54d 100644 --- a/packages/nutui-react-cli/package.json +++ b/packages/nutui-react-cli/package.json @@ -1,6 +1,6 @@ { "name": "@nutui/nutui-react-cli", - "version": "4.0.0", + "version": "4.1.0", "type": "module", "description": "NutUI React 面向 AI Coding 的离线知识查询 CLI:list / info / doc / demo / token。", "keywords": [ diff --git a/packages/nutui-react-cli/scripts/prepare-data.mjs b/packages/nutui-react-cli/scripts/prepare-data.mjs index dd2a141ddc..0991c6a6ff 100644 --- a/packages/nutui-react-cli/scripts/prepare-data.mjs +++ b/packages/nutui-react-cli/scripts/prepare-data.mjs @@ -1,15 +1,19 @@ -// @nutui/nutui-react-cli(H5)的 data 快照生成:委托 core 的 prepareData,注入 H5 端参数。 +// @nutui/nutui-react-cli(H5)的多版本 data 快照生成:委托 core 的 prepareAllVersions,注入 H5 端参数。 // DO NOT manual edit the output (data/). Run: pnpm run prepare-data import path from 'node:path' import { fileURLToPath } from 'node:url' -import { prepareData } from '@nutui/nutui-react-cli-core/scripts/prepare-data.mjs' +import { prepareAllVersions } from '@nutui/nutui-react-cli-core/scripts/prepare-data.mjs' const __dirname = path.dirname(fileURLToPath(import.meta.url)) const PKG_DIR = path.resolve(__dirname, '..') -prepareData({ +await prepareAllVersions({ pkgDir: PKG_DIR, docKeys: { zh: 'h5', en: 'enUS' }, demoKey: 'h5', apiField: 'api', + // H5 端迁移文档(随 v4 手写维护,v3 tag 无,缺失静默跳过)。 + migrationDocs: { + 'from-v3': 'src/sites/sites-react/doc/docs/react/migrate-from-v3.md', + }, }) diff --git a/packages/nutui-react-cli/skills/nutui-react-v3-to-v4/README.md b/packages/nutui-react-cli/skills/nutui-react-v3-to-v4/README.md new file mode 100644 index 0000000000..936603c7e0 --- /dev/null +++ b/packages/nutui-react-cli/skills/nutui-react-v3-to-v4/README.md @@ -0,0 +1,29 @@ +# NutUI React v3 → v4 Upgrade + +把项目从 NutUI React v3(`@nutui/nutui-react` 3.x)升级到 v4(4.x)。 + +与常见的大版本升级不同:v3→v4 **几乎没有 Props 增删或改名**,破坏性变更集中在**样式 / CSS 类名 / Design Token / 枚举值 / 默认值**(如 Empty 的 `size`/`status`、Popover 的 `theme` 默认值、Toast 的 `duration`)。这些变更**不会触发编译错误**,漏改会静默出错。本 skill 用离线 CLI 的 `migrate` / `diff` 命令定位真正需要改的组件,以官方迁移文档为准逐处改写,避免臆造 API 改名或漏掉默认值变更。 + +## 安装 + +运行时通过 `npx` 免安装调用 CLI,无需预装: + +```bash +npx skills add jdf2e/nutui-react --skill nutui-react-v3-to-v4 +``` + +## 适用场景 + +- **系统性完成 v3 → v4 升级**:按依赖升级 → 扫描盘点 → 逐组件改写 → 验证的流程,把项目从 `@nutui/nutui-react` 3.x 升到 4.x。 +- **定位真正需要改的组件**:用 `nutui-react migrate 3 4 --apply ./src` 扫描项目,只列出项目实际用到、且有破坏性变更的组件,避免对全部组件盲目排查。 +- **精确核对 API 差异**:用 `nutui-react diff 3 4 ` 拿到两版 Props 的枚举值 / 默认值 / 类型的精确 `旧→新`,补齐迁移文档没写到的 API 级差异。 +- **处理枚举值 / 默认值变更**:识别 Empty `size`/`status`、Popover `theme`、Toast `duration` 等默认值变更——它们不报编译错,必须改代码。 +- **核对自定义样式覆盖**:grep 项目里针对 `.nut-*` 类名或 `--nutui-*` 变量的自定义样式,对照 CSS 类名 / token 变更逐处修正。 + +## 依赖的 CLI 命令 + +- `nutui-react migrate 3 4 [--component X] [--apply ./src]` —— 官方迁移文档(按组件切分 / 项目扫描)。 +- `nutui-react diff 3 4 [Component]` —— 跨版本 Props 实测差异。 +- `nutui-react doc ` / `token ` —— 完整文档与 Design Token 交叉核对。 + +全部离线、随包分发、支持 `--format json`。 diff --git a/packages/nutui-react-cli/skills/nutui-react-v3-to-v4/SKILL.en.md b/packages/nutui-react-cli/skills/nutui-react-v3-to-v4/SKILL.en.md new file mode 100644 index 0000000000..b90ba832ea --- /dev/null +++ b/packages/nutui-react-cli/skills/nutui-react-v3-to-v4/SKILL.en.md @@ -0,0 +1,138 @@ +--- +name: nutui-react-v3-to-v4 +description: > + Use when upgrading a project from NutUI React v3 (@nutui/nutui-react 3.x, H5) to v4 (4.x). Triggers on "upgrade NutUI React from v3 to v4", "migrate nutui v3 to v4", "bump @nutui/nutui-react to 4.0". Unlike typical major upgrades, v3→v4 has almost no Props additions/removals/renames — breaking changes concentrate in **styles / CSS class names / Design Tokens / enum values / default values** (e.g. Empty's size/status, Popover's theme). This skill uses the offline CLI's migrate / diff commands to pinpoint which components actually need changes, rewriting each per the official migration doc — avoiding invented API renames or missed default-value changes. +allowed-tools: + - Bash(nutui-react *) + - Bash(npx -y @nutui/nutui-react-cli *) + - Bash(which nutui-react) + - Bash(grep *) +--- + +# NutUI React v3 → v4 Upgrade + +You are upgrading a project from `@nutui/nutui-react` **3.x** to **4.x** (H5). + +**Remember this counter-intuitive fact first**: v3→v4 has **almost no Props additions / renames** — most components' APIs are identical across the two versions. The real breaking changes concentrate in four dimensions: + +1. **Enum / default-value changes** (most code-impacting) — e.g. Empty's `size`, `status`, Popover's `theme` default. These do **not** auto-fall-back, and the compiler won't flag them — missing one fails silently. +2. **CSS class-name changes** — if the project has custom style overrides targeting `.nut-xxx` classes, a renamed class silently breaks them. +3. **Design Token changes** — added / removed / redefaulted `var(--nutui-*)` variables. +4. **Visual-spec adjustments** — spacing, font size, icon size. Usually no code change, but verify rendering after build. + +So: **don't assume upgrade = change imports or rename Props.** Use the CLI to find what each component actually changed, then fix each spot. + +## Supporting tools + +`@nutui/nutui-react-cli` — offline, metadata bundled. If not on PATH, call via npx: + +```bash +which nutui-react || echo "use: npx -y @nutui/nutui-react-cli " +``` + +Two key commands: + +- `nutui-react migrate 3 4` — the **official migration doc** (authoritative, hand-written, filled in per-component alongside v4). This is the **primary basis** for rewrites. +- `nutui-react diff 3 4 ` — **measured Props diff between the two versions' meta** (precise `old → new` for enums, defaults, types). Fills in API/default-level diffs the doc doesn't spell out. + +They're **complementary**: migrate explains "why & how to change" (incl. styles/classes/tokens), diff gives "what exactly differs at the API layer". Read both when changing a component. + +**Always pass `--format json` and parse it — don't regex the text output.** + +## Upgrade flow + +Execute in order. Don't skip the scan — it decides which few components you need to touch. + +### Phase 0 — Dependency upgrade (do first) + +1. Bump deps to v4 (get user consent before installing): + - `@nutui/nutui-react@^4` + - icon package `@nutui/icons-react` (v4-compatible version) — if the project uses icons +2. Confirm lockfile update and `node_modules` reinstall. + +### Phase 1 — Scan & inventory + +Use `--apply` to scan the project and get "which components have breaking changes to handle" in one step: + +```bash +nutui-react migrate 3 4 --apply ./src --format json +``` + +Focus on three fields: + +- `matchedComponents` — **components the project uses AND that have breaking changes**. This is your worklist. +- `componentsWithoutBreakingChanges` — used but not listed in the migration doc (most components; usually no change needed). +- `steps[].guide` — the migration doc text for each component to handle. + +If `matchedComponents` is empty: per the current doc, the project's components need no code changes — proceed to Phase 3 verification (still worth checking styles). + +Extra scan: if the project has **custom CSS overrides** (targeting `.nut-*` classes or `--nutui-*` variables), grep them — they're the high-risk spots for class/token changes: + +```bash +grep -rn "\.nut-" src --include=*.css --include=*.scss --include=*.less +grep -rn -- "--nutui-" src --include=*.css --include=*.scss --include=*.less +``` + +### Phase 2 — Rewrite per component + +For **each** component in `matchedComponents`: + +```bash +# 1. Read the official migration note (why & how; styles/classes/tokens) +nutui-react migrate 3 4 --component Empty --format json + +# 2. See precise API diff (enums, defaults, types old → new) +nutui-react diff 3 4 Empty --format json + +# 3. Cross-check full doc / tokens when needed +nutui-react doc Empty --format json +nutui-react token Empty --format json +``` + +Then rewrite per the migration note. Focus on **enum / default / class-name / token** changes; don't invent Props renames. + +### Phase 3 — Verify + +- Build the project and confirm it compiles. +- **Compiling ≠ correct** — enum/default changes don't raise compile errors. Verify actual rendering of every component you touched in Phase 1/2. +- Report: which components were changed, which custom style overrides need review, which you recommend the user eyeball on device/browser. + +## Known breaking changes (as of current doc — always defer to live `migrate` output) + +The migration doc is filled in continuously through v4 beta; below are currently-covered components. **Always run `migrate 3 4` for the latest list; don't rely on this table alone.** + +### Empty — enums + defaults + class names, all breaking + +- `size`: removes `base`/`small`, now `full`/`half`/`partial`; **default `base` → `half`**. + - `size="base"` → `full` (full-page) or `partial` (inline); `size="small"` → `half`. +- `status`: removes `empty`/`error`; **default `empty` → `network`**. + - `status="empty"` → `search` (or a scenario enum); `status="error"` → `network` (or pass a custom `image`). +- CSS classes: `.nut-empty-base`/`.nut-empty-small` → `.nut-empty--full`/`--half`/`--partial`; image container → `.nut-empty-image`. +- tokens: old `--nutui-empty-image-size` etc. removed; use per-size variables. + +### Popover — defaults + class names, breaking + +- New `type` (`status`/`description`), default `status`. +- **`theme` default `light` → `dark`**: v3 code relying on the light default must add `theme="light"`; v3 `theme="dark"` can drop the prop. +- CSS classes: removes `.nut-popover-dark` (dark is now default); light style uses `.nut-popover-light`. +- tokens: several `--nutui-popover-*` defaults adjusted. + +### Toast — defaults + class name + +- **Default `duration` `2s` → `3s`**: to keep 2s, pass `duration: 2` explicitly or `Toast.config({ duration: 2 })` globally. +- Removes the misspelled helper class `.nut-toast-inner-descrption` (**no replacement class**; delete or move overrides to `.nut-toast-inner`). + +### Radio / Checkbox — pure style + +- Radio: hotspot tweak when no text (adds `.nut-radio-nolabel`), removes checked shadow. +- Checkbox: dark-mode color fixes. +- Usually no code change, but review related style overrides. + +## Key rules + +1. **Scan first, then query per component.** `migrate 3 4 --apply ./src` gives the worklist; `migrate --component X` + `diff 3 4 X` per component before changing. +2. **Breaking changes are in styles/classes/tokens/enums/defaults, not Props names.** Don't invent API renames. +3. **Enum/default changes must be fixed and won't raise compile errors** — Empty `size`/`status`, Popover `theme`, Toast `duration` are the danger zones; missing one fails silently. +4. **Always `--format json`.** +5. **Don't use `nutui-codemod` for v3→v4** — its rules are for v2→v3 (component/Props renames) and will mis-edit if run. +6. **Compiling isn't done** — eyeball rendering of changed components, hand off spots needing human confirmation. diff --git a/packages/nutui-react-cli/skills/nutui-react-v3-to-v4/SKILL.md b/packages/nutui-react-cli/skills/nutui-react-v3-to-v4/SKILL.md new file mode 100644 index 0000000000..6b1a4e4fa1 --- /dev/null +++ b/packages/nutui-react-cli/skills/nutui-react-v3-to-v4/SKILL.md @@ -0,0 +1,138 @@ +--- +name: nutui-react-v3-to-v4 +description: > + 当需要把项目从 NutUI React v3(@nutui/nutui-react 3.x)升级到 v4(4.x)时使用。触发场景如「把 NutUI React 从 v3 升级到 v4」「nutui v3 迁移 v4」「升级 @nutui/nutui-react 到 4.0」。与常见的大版本升级不同:v3→v4 几乎没有 Props 增删或改名,破坏性变更集中在**样式 / CSS 类名 / Design Token / 枚举值 / 默认值**(如 Empty 的 size/status、Popover 的 theme)。本 skill 用离线 CLI 的 migrate / diff 命令定位真正需要改的组件,以官方迁移文档为准逐处改写,避免臆造 API 改名或漏掉默认值变更。 +allowed-tools: + - Bash(nutui-react *) + - Bash(npx -y @nutui/nutui-react-cli *) + - Bash(which nutui-react) + - Bash(grep *) +--- + +# NutUI React v3 → v4 升级 + +你负责把项目从 `@nutui/nutui-react` **3.x** 升级到 **4.x**。 + +**先记住这个反直觉的事实**:v3→v4 **几乎没有 Props 增删 / 改名**——大多数组件的 API 在两版之间完全一致。真正的破坏性变更集中在四个维度: + +1. **枚举值 / 默认值变更**(最需要改代码)——如 Empty 的 `size`、`status`,Popover 的 `theme` 默认值。这些**不会自动兼容**,且编译器不会报错,漏改会静默出错。 +2. **CSS 类名变更**——如果项目里有针对 `.nut-xxx` 类名的自定义样式覆盖,类名变了样式就失效。 +3. **Design Token 变更**——`var(--nutui-*)` 变量的新增 / 移除 / 默认值调整。 +4. **视觉规格调整**——间距、字号、图标尺寸等,通常无需改代码,但需构建后人工核对。 + +所以:**不要假设升级 = 改 import 或改 Props 名。** 要靠 CLI 查出每个组件到底改了什么维度,再逐处改。 + +## 支撑工具 + +`@nutui/nutui-react-cli` —— 离线、元数据随包分发。若不在 PATH 上用 npx 调用: + +```bash +which nutui-react || echo "use: npx -y @nutui/nutui-react-cli " +``` + +两个关键命令: + +- `nutui-react migrate 3 4` —— **官方迁移文档**(权威、手写、随 v4 逐组件补充)。这是改写的**主依据**。 +- `nutui-react diff 3 4 ` —— **两版 meta 的 Props 实测差异**(枚举值、默认值、类型的精确 `旧→新`)。补齐文档没写到的 API/默认值级差异。 + +二者**互补**:migrate 讲"为什么改、怎么改"(含样式/类名/token),diff 给"API 层精确差了什么"。改一个组件时两个都看。 + +**始终传 `--format json` 解析结构化输出,不要正则抓文本。** + +## 升级流程 + +按序执行。不要跳过扫描——它决定了你只需处理哪几个组件。 + +### 阶段 0 —— 依赖升级(先做) + +1. 升级依赖到 v4(执行安装前先征求用户同意): + - `@nutui/nutui-react@^4` + - 图标包 `@nutui/icons-react`(v4 配套版本)——若项目用到图标 +2. 确认锁文件更新、`node_modules` 重装。 + +### 阶段 1 —— 扫描盘点 + +用 `--apply` 扫描项目,一步得到"哪些组件有破坏性变更需要处理": + +```bash +nutui-react migrate 3 4 --apply ./src --format json +``` + +输出里关注三个字段: + +- `matchedComponents` —— **项目用到、且有破坏性变更的组件**。这是你的工作清单。 +- `componentsWithoutBreakingChanges` —— 项目用到、但迁移文档未列破坏性变更的组件(多数组件在此,通常无需改)。 +- `steps[].guide` —— 每个待处理组件的迁移说明原文。 + +若 `matchedComponents` 为空:按当前迁移文档,项目所用组件无需改代码——直接进阶段 3 验证即可(仍建议核对样式)。 + +补充扫描:如果项目有**自定义 CSS 覆盖**(针对 `.nut-*` 类名或 `--nutui-*` 变量),grep 出来,它们是类名/token 变更的高风险点: + +```bash +grep -rn "\.nut-" src --include=*.css --include=*.scss --include=*.less +grep -rn -- "--nutui-" src --include=*.css --include=*.scss --include=*.less +``` + +### 阶段 2 —— 逐组件改写 + +对 `matchedComponents` 里的**每个**组件: + +```bash +# 1. 读官方迁移说明(为什么改、样式/类名/token 怎么变) +nutui-react migrate 3 4 --component Empty --format json + +# 2. 看 API 层精确差异(枚举值、默认值、类型的 旧→新) +nutui-react diff 3 4 Empty --format json + +# 3. 需要时查完整文档 / token 交叉核对 +nutui-react doc Empty --format json +nutui-react token Empty --format json +``` + +然后按迁移说明逐处改写。以**枚举值 / 默认值 / 类名 / token** 为主,不要臆造 Props 改名。 + +### 阶段 3 —— 验证 + +- 构建项目确认编译通过。 +- **编译通过 ≠ 视觉/行为正确**——枚举值/默认值变更不会报编译错。逐个核对阶段 1/2 改过的组件的实际渲染。 +- 报告:哪些组件已改、哪些自定义样式覆盖需复核、哪些建议用户在真机/浏览器里目视确认。 + +## 已知破坏性变更(截至当前文档,务必以 `migrate` 命令实时输出为准) + +迁移文档随 v4 beta 持续补充,以下为当前已覆盖的组件与要点。**始终跑 `migrate 3 4` 拿最新清单,不要只依赖下表。** + +### Empty —— 枚举值 + 默认值 + 类名,全部不兼容 + +- `size`:移除 `base`/`small`,改为 `full`/`half`/`partial`;**默认值 `base` → `half`**。 + - `size="base"` → 整页用 `full`、局部用 `partial`;`size="small"` → `half`。 +- `status`:移除 `empty`/`error`;**默认值 `empty` → `network`**。 + - `status="empty"` → `search`(或按场景选枚举);`status="error"` → `network`(或用 `image` 传自定义插图)。 +- CSS 类名:`.nut-empty-base`/`.nut-empty-small` → `.nut-empty--full`/`--half`/`--partial`;插图容器 → `.nut-empty-image`。 +- token:旧 `--nutui-empty-image-size` 等已移除,改用分尺寸变量。 + +### Popover —— 默认值 + 类名,不兼容 + +- 新增 `type`(`status`/`description`),默认 `status`。 +- **`theme` 默认值 `light` → `dark`**:v3 默认明亮风格的,需显式加 `theme="light"`;v3 写 `theme="dark"` 的可移除该属性。 +- CSS 类名:移除 `.nut-popover-dark`(深色已是默认);明亮风格用 `.nut-popover-light`。 +- token:多个 `--nutui-popover-*` 默认值调整。 + +### Toast —— 默认值 + 类名 + +- **默认 `duration` `2s` → `3s`**:要维持 2s 显式传 `duration: 2` 或全局 `Toast.config({ duration: 2 })`。 +- 移除拼错的辅助类名 `.nut-toast-inner-descrption`(**无替代类名**,若有针对它的样式覆盖需删除/改到 `.nut-toast-inner`)。 + +### Radio / Checkbox —— 纯样式 + +- Radio:无文本时热区调整(新增 `.nut-radio-nolabel`)、去除选中投影。 +- Checkbox:暗黑模式色彩修复。 +- 一般无需改代码,但如有相关样式覆盖需核对。 + +## 核心规则 + +1. **先扫描,再逐组件查。** `migrate 3 4 --apply ./src` 给工作清单,逐个 `migrate --component X` + `diff 3 4 X` 再改。 +2. **破坏性变更主要在样式/类名/token/枚举/默认值,不在 Props 名。** 不要臆造 API 改名。 +3. **枚举值/默认值变更必须改代码且不报编译错**——Empty `size`/`status`、Popover `theme`、Toast `duration` 是重灾区,漏改会静默出错。 +4. **始终 `--format json`。** +5. **不要用 `nutui-codemod` 做 v3→v4** —— 那个包的规则是 v2→v3 的(组件/Props 改名),与 v3→v4 不匹配,跑了会误改。 +6. **构建通过不代表升级完成** —— 对改过的组件目视核对渲染,把需人工确认处交给用户。 diff --git a/packages/nutui-react-cli/skills/nutui-react/SKILL.en.md b/packages/nutui-react-cli/skills/nutui-react/SKILL.en.md index ddbc3dfdac..e5071ca4cd 100644 --- a/packages/nutui-react-cli/skills/nutui-react/SKILL.en.md +++ b/packages/nutui-react-cli/skills/nutui-react/SKILL.en.md @@ -123,9 +123,10 @@ This provides 5 tools (`nutui_list`, `nutui_info`, `nutui_doc`, `nutui_demo`, `n | Flag | Purpose | | --- | --- | | `--format, -f ` | Output format; agents should prefer `json` (default: `text`) | +| `--nutui-version, --nv ` | Target NutUI version (e.g. `3`, `3.1.0`, `4.0.0-beta.7`); auto-detected when omitted | | `--lang, -l ` | Doc language for `doc` / `mcp` (default: `zh`) | | `--help, -h` | Show help | -| `--version, -v` | Print CLI version | +| `--version, -v` | Print the CLI's own version (not the NutUI version) | ## Key Rules @@ -133,3 +134,4 @@ This provides 5 tools (`nutui_list`, `nutui_info`, `nutui_doc`, `nutui_demo`, `n 2. **Use `--format json`** — Every command supports it. Parse the JSON output rather than regex-matching the human-readable text. 3. **Confirm the component exists** — If unsure of the exact name, run `nutui-react list` or rely on the CLI's "did you mean" suggestion instead of importing a guessed name. 4. **Use Design Tokens for styling** — NutUI uses `nut-` flat BEM class names and the `var(--nutui-*)` token system. When customizing appearance, query `nutui-react token` and use tokens rather than hardcoding colors or spacing. +5. **Prefer auto-detected versions** — The CLI bundles v3 / v4 multi-version snapshots. When working inside a project that depends on NutUI, prefer **not** passing `--nutui-version` — let the CLI infer it from the project's `node_modules` / `package.json`. Only pass `--nv ` when the user explicitly asks about a specific version or you are outside a project context. The `_meta.version` / `_meta.source` in the output tells you which version was actually resolved. diff --git a/packages/nutui-react-cli/skills/nutui-react/SKILL.md b/packages/nutui-react-cli/skills/nutui-react/SKILL.md index 8b813f0400..c1b5ccae1b 100644 --- a/packages/nutui-react-cli/skills/nutui-react/SKILL.md +++ b/packages/nutui-react-cli/skills/nutui-react/SKILL.md @@ -119,9 +119,10 @@ nutui-react token Button --format json | 参数 | 用途 | | --- | --- | | `--format, -f ` | 输出格式;agent 应优先使用 `json`(默认:`text`) | +| `--nutui-version, --nv ` | 目标 NutUI 版本(如 `3`、`3.1.0`、`4.0.0-beta.7`);省略则自动检测 | | `--lang, -l ` | `doc` / `mcp` 的文档语言(默认:`zh`) | | `--help, -h` | 显示帮助 | -| `--version, -v` | 打印 CLI 版本 | +| `--version, -v` | 打印 CLI 自身版本(不是 NutUI 版本) | ## 核心规则 @@ -129,3 +130,4 @@ nutui-react token Button --format json 2. **使用 `--format json`** —— 每个命令都支持它。解析 JSON 输出,而不是用正则匹配人类可读的文本。 3. **确认组件存在** —— 如果不确定确切名称,运行 `nutui-react list`,或借助 CLI 的「你是不是想找」建议,而不是导入一个猜测的名称。 4. **使用 Design Token 做样式** —— NutUI 采用 `nut-` 扁平 BEM 类名和 `var(--nutui-*)` 变量体系。定制外观时,查询 `nutui-react token` 并使用 Token,而不是硬编码颜色或间距。 +5. **版本优先自动检测** —— CLI 内置 v3 / v4 多版本快照。在有 NutUI 依赖的项目里工作时,优先**不传** `--nutui-version`,让 CLI 从项目 `node_modules` / `package.json` 自动推断;只有当用户明确要查某个特定版本、或不在项目上下文中时,才显式传 `--nv `。输出里的 `_meta.version` / `_meta.source` 会告诉你实际命中的版本。 diff --git a/packages/nutui-react-cli/src/cli.ts b/packages/nutui-react-cli/src/cli.ts index 217f0e0eb7..37e0ee1828 100644 --- a/packages/nutui-react-cli/src/cli.ts +++ b/packages/nutui-react-cli/src/cli.ts @@ -12,6 +12,7 @@ const dataDir = fileURLToPath(new URL('../data/', import.meta.url)) const config: CliConfig = { binName: 'nutui-react', cliPkgName: '@nutui/nutui-react-cli', + npmPackageName: '@nutui/nutui-react', platform: 'h5', libLabel: 'NutUI React(H5)', demoLabel: 'H5', diff --git a/packages/nutui-react-cli/tsup.config.ts b/packages/nutui-react-cli/tsup.config.ts index 9a2ad73922..f57de67ec0 100644 --- a/packages/nutui-react-cli/tsup.config.ts +++ b/packages/nutui-react-cli/tsup.config.ts @@ -14,6 +14,7 @@ export default defineConfig({ // core 为 workspace 内 private 包(不发布),必须一并 bundle 进产物。 noExternal: [ 'yargs', + 'semver', '@modelcontextprotocol/sdk', '@nutui/nutui-react-cli-core', ], diff --git a/packages/nutui-react-taro-cli/CHANGELOG.md b/packages/nutui-react-taro-cli/CHANGELOG.md index c3d777bb6e..33371848eb 100644 --- a/packages/nutui-react-taro-cli/CHANGELOG.md +++ b/packages/nutui-react-taro-cli/CHANGELOG.md @@ -2,6 +2,13 @@ 本包版本号自 `4.0.0` 起与组件包 `@nutui/nutui-react-taro` 对齐,便于未来支持多版本查询。 +## v4.1.0 + +`2026-09-04` + +- feat: 新增多版本知识查询能力 +- feat: 新增 v3 -> v4 迁移的能力 + ## v4.0.0 `2026-08-21` diff --git a/packages/nutui-react-taro-cli/README.md b/packages/nutui-react-taro-cli/README.md index b4feec3e4c..886f6cb58a 100644 --- a/packages/nutui-react-taro-cli/README.md +++ b/packages/nutui-react-taro-cli/README.md @@ -36,9 +36,16 @@ nutui-react-taro info Button --format json | `nutui-react-taro doc ` | 查看组件完整文档(中文) | | `nutui-react-taro demo [name]` | 省略 `name` 列出全部示例;指定 `name`(如 `demo1`)输出源码 | | `nutui-react-taro token [Component]` | 查看 Design Token;省略组件名则列出全局 token | +| `nutui-react-taro migrate [from] [to]` | 输出 vN→vM 迁移指南(默认 `3 4`);`--component ` 看单组件,`--apply ` 扫描项目生成迁移提示 | +| `nutui-react-taro diff [Component]` | 跨版本 Props 差异对比(新增 / 移除 / 类型或默认值变更) | | `nutui-react-taro mcp` | 启动本地 MCP 服务(stdio),供 Claude Code / Cursor / VS Code / Codex 等 IDE 调用 | -全局选项:`--format, -f `(默认 `text`)、`--help, -h`、`--version, -v`。 +全局选项: + +- `--format, -f `(默认 `text`) +- `--nutui-version, --nv `(目标 NutUI 版本,见下) +- `--help, -h` +- `--version, -v`(CLI 自身版本) 示例: @@ -49,13 +56,55 @@ nutui-react-taro doc Button nutui-react-taro demo Button # 列出示例 nutui-react-taro demo Button demo1 # 查看某个示例源码 nutui-react-taro token Button +nutui-react-taro migrate 3 4 # v3→v4 迁移指南 +nutui-react-taro migrate 3 4 --apply ./src --format json # 扫描项目,生成给 Agent 的迁移提示 +nutui-react-taro diff 3.1.0 4.0.0-beta.7 Empty # 跨版本 Props 差异 +``` + +## 多版本 + +CLI 随包内置多个 NutUI 大版本的离线快照(当前:`v3.0.20`、`v3.1.0`、`v4.0.0-beta.7`),可对同一台机器上不同 NutUI 版本给出对应的组件知识。 + +用 `--nutui-version`(别名 `--nv`)指定目标版本,支持 `3`、`3.1.0`、`4.0.0-beta.7` 等写法: + +```bash +nutui-react-taro --nv 3.1.0 info Button +nutui-react-taro --nv 3 list +nutui-react-taro --nv 4.0.0-beta.7 doc Cell +``` + +按以下顺序**自动检测**目标版本: + +1. `--nutui-version ` 显式指定; +2. 项目 `node_modules/@nutui/nutui-react-taro/package.json` 的实际安装版本; +3. 项目 `package.json` 的 `dependencies` / `devDependencies` / `peerDependencies` 声明(兼容 `^3.1.0`、`~3.1.0` 等); +4. 兜底到默认大版本(`v4`)的 latest。 + +版本路由按 `major.minor` 粒度:请求 `3.0.5` 会落到该 minor 系列的最高 patch 快照 `v3.0.20`;请求的 minor 若高于已有,回退到该 major 最近的旧 minor。每次查询的输出都会标明实际命中的版本与来源。 + +> 在项目里能自动推断版本时,优先不显式传 `--nutui-version`,让 CLI 自行检测更省心。`-v / --version` 语义保持不变,仍输出 CLI 自身版本。 + +## 版本迁移(v3 → v4) + +两个命令支撑从 v3 升级到 v4,数据均随包离线分发: + +- `nutui-react-taro migrate 3 4` —— 输出**官方迁移文档**(从主仓 Taro 端 `migrate-from-v3.md` 抽取,随 v4 逐组件补充)。`--component ` 只看某组件;`--apply ` 扫描项目、只输出实际用到组件的迁移步骤,并生成一段给 Code Agent 的定向提示。 +- `nutui-react-taro diff [Component]` —— 实时对比两个版本快照的 Props,列出新增 / 移除 / 类型或默认值变更(精确 `旧→新`)。 + +二者**互补**:`migrate` 讲"为什么改、怎么改"(含样式 / CSS 类名 / Design Token),`diff` 给"API 层精确差了什么"。配套的 `nutui-react-taro-v3-to-v4` Skill 会编排整个升级流程(见下)。 + +```bash +nutui-react-taro migrate 3 4 --format json # 全量迁移清单 +nutui-react-taro migrate 3 4 --component Empty # 只看 Empty +nutui-react-taro migrate 3 4 --apply ./src --format json # 扫描项目 + 生成 Agent 提示 +nutui-react-taro diff 3 4 Empty --format json # Empty 的 Props 差异 ``` ## MCP(IDE 集成) CLI 是「Agent 主动敲命令」,MCP 则把同一份能力注册成 IDE 原生工具,让 Claude Code / Cursor / VS Code / Codex 在对话中**按需自动调用**,无需拼命令行字符串。二者复用同一份离线 meta 快照,等于给同一个知识库套了两种调用协议。 -`nutui-react-taro mcp` 启动一个 stdio MCP 服务,暴露 5 个只读工具与 2 个提示词: +`nutui-react-taro mcp` 启动一个 stdio MCP 服务,暴露 7 个只读工具与 2 个提示词: | 工具 | 说明 | | --- | --- | @@ -64,6 +113,8 @@ CLI 是「Agent 主动敲命令」,MCP 则把同一份能力注册成 IDE 原 | `nutui_doc` | 组件完整文档(中文) | | `nutui_demo` | Taro 示例列表 / 源码 | | `nutui_token` | Design Token(全局 / 组件级) | +| `nutui_migrate` | vN→vM 迁移指南(可按组件 / 扫描目录) | +| `nutui_diff` | 跨版本 Props 差异对比 | | 提示词 | 说明 | | --- | --- | @@ -107,6 +158,14 @@ npx skills add jdf2e/nutui-react --skill nutui-react-taro 兼容 Claude Code / Cursor / VS Code / Codex 等所有支持 [skills](https://github.com/vercel-labs/skills) 协议的 Agent。安装后,Agent 在遇到 NutUI React Taro 相关任务时会自动遵循「先查后写」的流程。 +### 升级 Skill:v3 → v4 + +本包还内置一份 [升级 Skill](./skills/nutui-react-taro-v3-to-v4/SKILL.md),指导 Agent 把 Taro 项目从 NutUI React Taro v3 升级到 v4。它编排「依赖升级 → `migrate --apply` 扫描项目 → 逐组件读 `migrate` + `diff` 改写 → 构建验证」的完整流程,并把 v3→v4 的关键事实讲清楚:破坏性变更集中在**样式 / CSS 类名 / Design Token / 枚举值 / 默认值**(如 Empty 的 `size`/`status`、Popover 的 `theme`、Toast 的 `duration`)。 + +```bash +npx skills add jdf2e/nutui-react --skill nutui-react-taro-v3-to-v4 +``` + ## 本地开发 ```bash diff --git a/packages/nutui-react-taro-cli/docs/mcp.md b/packages/nutui-react-taro-cli/docs/mcp.md index e5a8dba56a..649c06079a 100644 --- a/packages/nutui-react-taro-cli/docs/mcp.md +++ b/packages/nutui-react-taro-cli/docs/mcp.md @@ -1,10 +1,10 @@ # NutUI Taro Knowledge -通过 Model Context Protocol (MCP) 教 AI 工具中使用 NutUI-React(Taro 多端)。 +通过 Model Context Protocol (MCP) 教 AI 工具使用 NutUI-React(Taro 多端)。 ## 启动 MCP 服务 -`nutui-react-taro mcp` 启动一个通过 stdio 通信的本地 MCP 服务,暴露 5 个只读工具与 2 个提示词。该命令不应在终端直接裸跑,而应在 AI 工具中配置(见下方)。 +`nutui-react-taro mcp` 启动一个通过 stdio 通信的本地 MCP 服务,暴露 7 个只读工具与 2 个提示词。该命令不应在终端直接裸跑,而应在 AI 工具中配置(见下方)。 ### 工具 @@ -12,18 +12,25 @@ | --- | --- | | `nutui_list` | 列出全部组件(可按分类筛选) | | `nutui_info` | 获取组件的 Props 规格 | -| `nutui_doc` | 获取组件完整文档(仅 `zh`,Taro 端暂无英文文档) | +| `nutui_doc` | 获取组件完整文档(中文) | | `nutui_demo` | 获取组件 Taro 示例列表 / 源码 | | `nutui_token` | 查询 Design Token(全局 / 组件级) | +| `nutui_migrate` | 获取大版本迁移指南;可按组件筛选或扫描项目目录 | +| `nutui_diff` | 对比两个版本快照的 Props 差异 | 所有工具均为只读、无副作用、不访问外部网络。 +前 5 个组件知识工具都接受可选入参 `nutuiVersion`(如 `3.1.0`、`4.0.0-beta.7`),用于指定目标 NutUI 大版本;省略时自动检测项目所用版本,检测不到则回退默认大版本。 + +- `nutui_migrate` 接受可选的 `from`、`to`、`component` 和 `applyDir` 参数; +- `applyDir` 只扫描目录并返回迁移步骤,不修改文件。`nutui_diff` 要求传入 `v1`、`v2`,也可用 `component` 限定单个组件。两者与 CLI 的 `migrate`、`diff` 命令返回同类结构化数据,可由 Agent 直接用于 v3 → v4 升级。 + ### 提示词 | 提示词 | 说明 | | --- | --- | -| `nutui-expert` | 把 Agent 定位为 NutUI-React(Taro 多端)专家(先查后写) | -| `nutui-page-generator` | 基于 NutUI-React(Taro 多端)组件生成完整可运行页面 | +| `nutui-expert` | 把 Agent 定位为 NutUI-React Taro 专家(先查后写) | +| `nutui-page-generator` | 基于 NutUI-React Taro 组件生成完整可运行页面 | ## 配置 diff --git a/packages/nutui-react-taro-cli/package.json b/packages/nutui-react-taro-cli/package.json index ff3eac3e44..c7632d9f04 100644 --- a/packages/nutui-react-taro-cli/package.json +++ b/packages/nutui-react-taro-cli/package.json @@ -1,6 +1,6 @@ { "name": "@nutui/nutui-react-taro-cli", - "version": "4.0.0", + "version": "4.1.0", "type": "module", "description": "NutUI React Taro 面向 AI Coding 的离线知识查询 CLI:list / info / doc / demo / token。", "keywords": [ diff --git a/packages/nutui-react-taro-cli/scripts/prepare-data.mjs b/packages/nutui-react-taro-cli/scripts/prepare-data.mjs index 447dad2527..a42f997e41 100644 --- a/packages/nutui-react-taro-cli/scripts/prepare-data.mjs +++ b/packages/nutui-react-taro-cli/scripts/prepare-data.mjs @@ -1,16 +1,20 @@ -// @nutui/nutui-react-taro-cli(Taro)的 data 快照生成:委托 core 的 prepareData,注入 Taro 端参数。 +// @nutui/nutui-react-taro-cli(Taro)的多版本 data 快照生成:委托 core 的 prepareAllVersions,注入 Taro 端参数。 // DO NOT manual edit the output (data/). Run: pnpm run prepare-data import path from 'node:path' import { fileURLToPath } from 'node:url' -import { prepareData } from '@nutui/nutui-react-cli-core/scripts/prepare-data.mjs' +import { prepareAllVersions } from '@nutui/nutui-react-cli-core/scripts/prepare-data.mjs' const __dirname = path.dirname(fileURLToPath(import.meta.url)) const PKG_DIR = path.resolve(__dirname, '..') -prepareData({ +await prepareAllVersions({ pkgDir: PKG_DIR, // Taro 端仅中文文档(doc.taro.md)。 docKeys: { zh: 'taro' }, demoKey: 'taro', apiField: 'apiTaro', + // Taro 端迁移文档(随 v4 手写维护,v3 tag 无,缺失静默跳过)。 + migrationDocs: { + 'from-v3': 'src/sites/sites-react/doc/docs/taro/migrate-from-v3.md', + }, }) diff --git a/packages/nutui-react-taro-cli/skills/nutui-react-taro-v3-to-v4/README.md b/packages/nutui-react-taro-cli/skills/nutui-react-taro-v3-to-v4/README.md new file mode 100644 index 0000000000..4ef0bb333e --- /dev/null +++ b/packages/nutui-react-taro-cli/skills/nutui-react-taro-v3-to-v4/README.md @@ -0,0 +1,29 @@ +# NutUI React Taro v3 → v4 Upgrade + +把项目从 NutUI React Taro v3(`@nutui/nutui-react-taro` 3.x,小程序 / 跨端)升级到 v4(4.x)。 + +与常见的大版本升级不同:v3→v4 **几乎没有 Props 增删或改名**,破坏性变更集中在**样式 / CSS 类名 / Design Token / 枚举值 / 默认值**(如 Empty 的 `size`/`status`、Popover 的 `theme` 默认值、Toast 的 `duration`)。这些变更**不会触发编译错误**,漏改会静默出错。本 skill 用离线 CLI 的 `migrate` / `diff` 命令定位真正需要改的组件,以官方迁移文档为准逐处改写,避免臆造 API 改名或漏掉默认值变更。 + +## 安装 + +运行时通过 `npx` 免安装调用 CLI,无需预装: + +```bash +npx skills add jdf2e/nutui-react --skill nutui-react-taro-v3-to-v4 +``` + +## 适用场景 + +- **系统性完成 v3 → v4 升级**:按依赖升级 → 扫描盘点 → 逐组件改写 → 验证的流程,把 Taro 项目从 `@nutui/nutui-react-taro` 3.x 升到 4.x。 +- **定位真正需要改的组件**:用 `nutui-react-taro migrate 3 4 --apply ./src` 扫描项目,只列出项目实际用到、且有破坏性变更的组件,避免对全部组件盲目排查。 +- **精确核对 API 差异**:用 `nutui-react-taro diff 3 4 ` 拿到两版 Props 的枚举值 / 默认值 / 类型的精确 `旧→新`,补齐迁移文档没写到的 API 级差异。 +- **处理枚举值 / 默认值变更**:识别 Empty `size`/`status`、Popover `theme`、Toast `duration` 等默认值变更——它们不报编译错,必须改代码。 +- **核对自定义样式覆盖**:grep 项目里针对 `.nut-*` 类名或 `--nutui-*` 变量的自定义样式,对照 CSS 类名 / token 变更逐处修正。 + +## 依赖的 CLI 命令 + +- `nutui-react-taro migrate 3 4 [--component X] [--apply ./src]` —— 官方迁移文档(按组件切分 / 项目扫描)。 +- `nutui-react-taro diff 3 4 [Component]` —— 跨版本 Props 实测差异。 +- `nutui-react-taro doc ` / `token ` —— 完整文档与 Design Token 交叉核对。 + +全部离线、随包分发、支持 `--format json`。 diff --git a/packages/nutui-react-taro-cli/skills/nutui-react-taro-v3-to-v4/SKILL.en.md b/packages/nutui-react-taro-cli/skills/nutui-react-taro-v3-to-v4/SKILL.en.md new file mode 100644 index 0000000000..5b17085ac6 --- /dev/null +++ b/packages/nutui-react-taro-cli/skills/nutui-react-taro-v3-to-v4/SKILL.en.md @@ -0,0 +1,138 @@ +--- +name: nutui-react-taro-v3-to-v4 +description: > + Use when upgrading a project from NutUI React Taro v3 (@nutui/nutui-react-taro 3.x, mini-program / cross-platform) to v4 (4.x). Triggers on "upgrade NutUI React Taro from v3 to v4", "migrate nutui taro v3 to v4", "bump @nutui/nutui-react-taro to 4.0". Unlike typical major upgrades, v3→v4 has almost no Props additions/removals/renames — breaking changes concentrate in **styles / CSS class names / Design Tokens / enum values / default values** (e.g. Empty's size/status, Popover's theme). This skill uses the offline CLI's migrate / diff commands to pinpoint which components actually need changes, rewriting each per the official migration doc — avoiding invented API renames or missed default-value changes. +allowed-tools: + - Bash(nutui-react-taro *) + - Bash(npx -y @nutui/nutui-react-taro-cli *) + - Bash(which nutui-react-taro) + - Bash(grep *) +--- + +# NutUI React Taro v3 → v4 Upgrade + +You are upgrading a project from `@nutui/nutui-react-taro` **3.x** to **4.x** (Taro mini-program / cross-platform). + +**Remember this counter-intuitive fact first**: v3→v4 has **almost no Props additions / renames** — most components' APIs are identical across the two versions. The real breaking changes concentrate in four dimensions: + +1. **Enum / default-value changes** (most code-impacting) — e.g. Empty's `size`, `status`, Popover's `theme` default. These do **not** auto-fall-back, and the compiler won't flag them — missing one fails silently. +2. **CSS class-name changes** — if the project has custom style overrides targeting `.nut-xxx` classes, a renamed class silently breaks them. +3. **Design Token changes** — added / removed / redefaulted `var(--nutui-*)` variables. +4. **Visual-spec adjustments** — spacing, font size, icon size. Usually no code change, but verify rendering after build. + +So: **don't assume upgrade = change imports or rename Props.** Use the CLI to find what each component actually changed, then fix each spot. + +## Supporting tools + +`@nutui/nutui-react-taro-cli` — offline, metadata bundled. If not on PATH, call via npx: + +```bash +which nutui-react-taro || echo "use: npx -y @nutui/nutui-react-taro-cli " +``` + +Two key commands: + +- `nutui-react-taro migrate 3 4` — the **official migration doc** (authoritative, hand-written, filled in per-component alongside v4). This is the **primary basis** for rewrites. +- `nutui-react-taro diff 3 4 ` — **measured Props diff between the two versions' meta** (precise `old → new` for enums, defaults, types). Fills in API/default-level diffs the doc doesn't spell out. + +They're **complementary**: migrate explains "why & how to change" (incl. styles/classes/tokens), diff gives "what exactly differs at the API layer". Read both when changing a component. + +**Always pass `--format json` and parse it — don't regex the text output.** + +## Upgrade flow + +Execute in order. Don't skip the scan — it decides which few components you need to touch. + +### Phase 0 — Dependency upgrade (do first) + +1. Bump deps to v4 (get user consent before installing): + - `@nutui/nutui-react-taro@^4` + - icon package `@nutui/icons-react-taro` (v4-compatible version) — if the project uses icons +2. Confirm lockfile update and `node_modules` reinstall. + +### Phase 1 — Scan & inventory + +Use `--apply` to scan the project and get "which components have breaking changes to handle" in one step: + +```bash +nutui-react-taro migrate 3 4 --apply ./src --format json +``` + +Focus on three fields: + +- `matchedComponents` — **components the project uses AND that have breaking changes**. This is your worklist. +- `componentsWithoutBreakingChanges` — used but not listed in the migration doc (most components; usually no change needed). +- `steps[].guide` — the migration doc text for each component to handle. + +If `matchedComponents` is empty: per the current doc, the project's components need no code changes — proceed to Phase 3 verification (still worth checking styles). + +Extra scan: if the project has **custom CSS overrides** (targeting `.nut-*` classes or `--nutui-*` variables), grep them — they're the high-risk spots for class/token changes: + +```bash +grep -rn "\.nut-" src --include=*.css --include=*.scss --include=*.less +grep -rn -- "--nutui-" src --include=*.css --include=*.scss --include=*.less +``` + +### Phase 2 — Rewrite per component + +For **each** component in `matchedComponents`: + +```bash +# 1. Read the official migration note (why & how; styles/classes/tokens) +nutui-react-taro migrate 3 4 --component Empty --format json + +# 2. See precise API diff (enums, defaults, types old → new) +nutui-react-taro diff 3 4 Empty --format json + +# 3. Cross-check full doc / tokens when needed +nutui-react-taro doc Empty --format json +nutui-react-taro token Empty --format json +``` + +Then rewrite per the migration note. Focus on **enum / default / class-name / token** changes; don't invent Props renames. + +### Phase 3 — Verify + +- Build the project and confirm it compiles (e.g. `taro build --type weapp` / `--type h5`). +- **Compiling ≠ correct** — enum/default changes don't raise compile errors. Verify actual rendering of every component you touched in Phase 1/2. +- Report: which components were changed, which custom style overrides need review, which you recommend the user eyeball on device/simulator. + +## Known breaking changes (as of current doc — always defer to live `migrate` output) + +The migration doc is filled in continuously through v4 beta; below are currently-covered components. **Always run `migrate 3 4` for the latest list; don't rely on this table alone.** + +### Empty — enums + defaults + class names, all breaking + +- `size`: removes `base`/`small`, now `full`/`half`/`partial`; **default `base` → `half`**. + - `size="base"` → `full` (full-page) or `partial` (inline); `size="small"` → `half`. +- `status`: removes `empty`/`error`; **default `empty` → `network`**. + - `status="empty"` → `search` (or a scenario enum); `status="error"` → `network` (or pass a custom `image`). +- CSS classes: `.nut-empty-base`/`.nut-empty-small` → `.nut-empty--full`/`--half`/`--partial`; image container → `.nut-empty-image`. +- tokens: old `--nutui-empty-image-size` etc. removed; use per-size variables. + +### Popover — defaults + class names, breaking + +- New `type` (`status`/`description`), default `status`. +- **`theme` default `light` → `dark`**: v3 code relying on the light default must add `theme="light"`; v3 `theme="dark"` can drop the prop. +- CSS classes: removes `.nut-popover-dark` (dark is now default); light style uses `.nut-popover-light`. +- tokens: several `--nutui-popover-*` defaults adjusted. + +### Toast — defaults + class name + +- **Default `duration` `2s` → `3s`**: to keep 2s, pass `duration: 2` explicitly or `Toast.config({ duration: 2 })` globally. +- Removes the misspelled helper class `.nut-toast-inner-descrption` (**no replacement class**; delete or move overrides to `.nut-toast-inner`). + +### Radio / Checkbox — pure style + +- Radio: hotspot tweak when no text (adds `.nut-radio-nolabel`), removes checked shadow. +- Checkbox: dark-mode color fixes. +- Usually no code change, but review related style overrides. + +## Key rules + +1. **Scan first, then query per component.** `migrate 3 4 --apply ./src` gives the worklist; `migrate --component X` + `diff 3 4 X` per component before changing. +2. **Breaking changes are in styles/classes/tokens/enums/defaults, not Props names.** Don't invent API renames. +3. **Enum/default changes must be fixed and won't raise compile errors** — Empty `size`/`status`, Popover `theme`, Toast `duration` are the danger zones; missing one fails silently. +4. **Always `--format json`.** +5. **Don't use `nutui-codemod` for v3→v4** — its rules are for v2→v3 (component/Props renames) and will mis-edit if run. +6. **Compiling isn't done** — eyeball rendering of changed components, hand off spots needing human confirmation. diff --git a/packages/nutui-react-taro-cli/skills/nutui-react-taro-v3-to-v4/SKILL.md b/packages/nutui-react-taro-cli/skills/nutui-react-taro-v3-to-v4/SKILL.md new file mode 100644 index 0000000000..6eaf0f26b2 --- /dev/null +++ b/packages/nutui-react-taro-cli/skills/nutui-react-taro-v3-to-v4/SKILL.md @@ -0,0 +1,138 @@ +--- +name: nutui-react-taro-v3-to-v4 +description: > + 当需要把项目从 NutUI React Taro v3(@nutui/nutui-react-taro 3.x,小程序 / 跨端)升级到 v4(4.x)时使用。触发场景如「把 NutUI React Taro 从 v3 升级到 v4」「nutui taro v3 迁移 v4」「升级 @nutui/nutui-react-taro 到 4.0」。与常见的大版本升级不同:v3→v4 几乎没有 Props 增删或改名,破坏性变更集中在**样式 / CSS 类名 / Design Token / 枚举值 / 默认值**(如 Empty 的 size/status、Popover 的 theme)。本 skill 用离线 CLI 的 migrate / diff 命令定位真正需要改的组件,以官方迁移文档为准逐处改写,避免臆造 API 改名或漏掉默认值变更。 +allowed-tools: + - Bash(nutui-react-taro *) + - Bash(npx -y @nutui/nutui-react-taro-cli *) + - Bash(which nutui-react-taro) + - Bash(grep *) +--- + +# NutUI React Taro v3 → v4 升级 + +你负责把项目从 `@nutui/nutui-react-taro` **3.x** 升级到 **4.x**(Taro 小程序 / 跨端)。 + +**先记住这个反直觉的事实**:v3→v4 **几乎没有 Props 增删 / 改名**——大多数组件的 API 在两版之间完全一致。真正的破坏性变更集中在四个维度: + +1. **枚举值 / 默认值变更**(最需要改代码)——如 Empty 的 `size`、`status`,Popover 的 `theme` 默认值。这些**不会自动兼容**,且编译器不会报错,漏改会静默出错。 +2. **CSS 类名变更**——如果项目里有针对 `.nut-xxx` 类名的自定义样式覆盖,类名变了样式就失效。 +3. **Design Token 变更**——`var(--nutui-*)` 变量的新增 / 移除 / 默认值调整。 +4. **视觉规格调整**——间距、字号、图标尺寸等,通常无需改代码,但需构建后人工核对。 + +所以:**不要假设升级 = 改 import 或改 Props 名。** 要靠 CLI 查出每个组件到底改了什么维度,再逐处改。 + +## 支撑工具 + +`@nutui/nutui-react-taro-cli` —— 离线、元数据随包分发。若不在 PATH 上用 npx 调用: + +```bash +which nutui-react-taro || echo "use: npx -y @nutui/nutui-react-taro-cli " +``` + +两个关键命令: + +- `nutui-react-taro migrate 3 4` —— **官方迁移文档**(权威、手写、随 v4 逐组件补充)。这是改写的**主依据**。 +- `nutui-react-taro diff 3 4 ` —— **两版 meta 的 Props 实测差异**(枚举值、默认值、类型的精确 `旧→新`)。补齐文档没写到的 API/默认值级差异。 + +二者**互补**:migrate 讲"为什么改、怎么改"(含样式/类名/token),diff 给"API 层精确差了什么"。改一个组件时两个都看。 + +**始终传 `--format json` 解析结构化输出,不要正则抓文本。** + +## 升级流程 + +按序执行。不要跳过扫描——它决定了你只需处理哪几个组件。 + +### 阶段 0 —— 依赖升级(先做) + +1. 升级依赖到 v4(执行安装前先征求用户同意): + - `@nutui/nutui-react-taro@^4` + - 图标包 `@nutui/icons-react-taro`(v4 配套版本)——若项目用到图标 +2. 确认锁文件更新、`node_modules` 重装。 + +### 阶段 1 —— 扫描盘点 + +用 `--apply` 扫描项目,一步得到"哪些组件有破坏性变更需要处理": + +```bash +nutui-react-taro migrate 3 4 --apply ./src --format json +``` + +输出里关注三个字段: + +- `matchedComponents` —— **项目用到、且有破坏性变更的组件**。这是你的工作清单。 +- `componentsWithoutBreakingChanges` —— 项目用到、但迁移文档未列破坏性变更的组件(多数组件在此,通常无需改)。 +- `steps[].guide` —— 每个待处理组件的迁移说明原文。 + +若 `matchedComponents` 为空:按当前迁移文档,项目所用组件无需改代码——直接进阶段 3 验证即可(仍建议核对样式)。 + +补充扫描:如果项目有**自定义 CSS 覆盖**(针对 `.nut-*` 类名或 `--nutui-*` 变量),grep 出来,它们是类名/token 变更的高风险点: + +```bash +grep -rn "\.nut-" src --include=*.css --include=*.scss --include=*.less +grep -rn -- "--nutui-" src --include=*.css --include=*.scss --include=*.less +``` + +### 阶段 2 —— 逐组件改写 + +对 `matchedComponents` 里的**每个**组件: + +```bash +# 1. 读官方迁移说明(为什么改、样式/类名/token 怎么变) +nutui-react-taro migrate 3 4 --component Empty --format json + +# 2. 看 API 层精确差异(枚举值、默认值、类型的 旧→新) +nutui-react-taro diff 3 4 Empty --format json + +# 3. 需要时查完整文档 / token 交叉核对 +nutui-react-taro doc Empty --format json +nutui-react-taro token Empty --format json +``` + +然后按迁移说明逐处改写。以**枚举值 / 默认值 / 类名 / token** 为主,不要臆造 Props 改名。 + +### 阶段 3 —— 验证 + +- 构建项目确认编译通过(如 `taro build --type weapp` / `--type h5`)。 +- **编译通过 ≠ 视觉/行为正确**——枚举值/默认值变更不会报编译错。逐个核对阶段 1/2 改过的组件的实际渲染。 +- 报告:哪些组件已改、哪些自定义样式覆盖需复核、哪些建议用户在真机/模拟器里目视确认。 + +## 已知破坏性变更(截至当前文档,务必以 `migrate` 命令实时输出为准) + +迁移文档随 v4 beta 持续补充,以下为当前已覆盖的组件与要点。**始终跑 `migrate 3 4` 拿最新清单,不要只依赖下表。** + +### Empty —— 枚举值 + 默认值 + 类名,全部不兼容 + +- `size`:移除 `base`/`small`,改为 `full`/`half`/`partial`;**默认值 `base` → `half`**。 + - `size="base"` → 整页用 `full`、局部用 `partial`;`size="small"` → `half`。 +- `status`:移除 `empty`/`error`;**默认值 `empty` → `network`**。 + - `status="empty"` → `search`(或按场景选枚举);`status="error"` → `network`(或用 `image` 传自定义插图)。 +- CSS 类名:`.nut-empty-base`/`.nut-empty-small` → `.nut-empty--full`/`--half`/`--partial`;插图容器 → `.nut-empty-image`。 +- token:旧 `--nutui-empty-image-size` 等已移除,改用分尺寸变量。 + +### Popover —— 默认值 + 类名,不兼容 + +- 新增 `type`(`status`/`description`),默认 `status`。 +- **`theme` 默认值 `light` → `dark`**:v3 默认明亮风格的,需显式加 `theme="light"`;v3 写 `theme="dark"` 的可移除该属性。 +- CSS 类名:移除 `.nut-popover-dark`(深色已是默认);明亮风格用 `.nut-popover-light`。 +- token:多个 `--nutui-popover-*` 默认值调整。 + +### Toast —— 默认值 + 类名 + +- **默认 `duration` `2s` → `3s`**:要维持 2s 显式传 `duration: 2` 或全局 `Toast.config({ duration: 2 })`。 +- 移除拼错的辅助类名 `.nut-toast-inner-descrption`(**无替代类名**,若有针对它的样式覆盖需删除/改到 `.nut-toast-inner`)。 + +### Radio / Checkbox —— 纯样式 + +- Radio:无文本时热区调整(新增 `.nut-radio-nolabel`)、去除选中投影。 +- Checkbox:暗黑模式色彩修复。 +- 一般无需改代码,但如有相关样式覆盖需核对。 + +## 核心规则 + +1. **先扫描,再逐组件查。** `migrate 3 4 --apply ./src` 给工作清单,逐个 `migrate --component X` + `diff 3 4 X` 再改。 +2. **破坏性变更主要在样式/类名/token/枚举/默认值,不在 Props 名。** 不要臆造 API 改名。 +3. **枚举值/默认值变更必须改代码且不报编译错**——Empty `size`/`status`、Popover `theme`、Toast `duration` 是重灾区,漏改会静默出错。 +4. **始终 `--format json`。** +5. **不要用 `nutui-codemod` 做 v3→v4** —— 那个包的规则是 v2→v3 的(组件/Props 改名),与 v3→v4 不匹配,跑了会误改。 +6. **构建通过不代表升级完成** —— 对改过的组件目视核对渲染,把需人工确认处交给用户。 diff --git a/packages/nutui-react-taro-cli/skills/nutui-react-taro/SKILL.en.md b/packages/nutui-react-taro-cli/skills/nutui-react-taro/SKILL.en.md index 687575e7fc..3ea38cda81 100644 --- a/packages/nutui-react-taro-cli/skills/nutui-react-taro/SKILL.en.md +++ b/packages/nutui-react-taro-cli/skills/nutui-react-taro/SKILL.en.md @@ -124,8 +124,9 @@ This provides 5 tools (`nutui_list`, `nutui_info`, `nutui_doc`, `nutui_demo`, `n | Flag | Purpose | | --- | --- | | `--format, -f ` | Output format; agents should prefer `json` (default: `text`) | +| `--nutui-version, --nv ` | Target NutUI version (e.g. `3`, `3.1.0`, `4.0.0-beta.7`); auto-detected when omitted | | `--help, -h` | Show help | -| `--version, -v` | Print CLI version | +| `--version, -v` | Print the CLI's own version (not the NutUI version) | ## Key Rules @@ -134,3 +135,4 @@ This provides 5 tools (`nutui_list`, `nutui_info`, `nutui_doc`, `nutui_demo`, `n 3. **Confirm the component exists** — If unsure of the exact name, run `nutui-react-taro list` or rely on the CLI's "did you mean" suggestion instead of importing a guessed name. 4. **Use Design Tokens for styling** — NutUI uses `nut-` flat BEM class names and the `var(--nutui-*)` token system. When customizing appearance, query `nutui-react-taro token` and use tokens rather than hardcoding colors or spacing. 5. **Import from `@nutui/nutui-react-taro`** — Not `@nutui/nutui-react`. This is the Taro (mini-program / cross-platform) package. +6. **Prefer auto-detected versions** — The CLI bundles v3 / v4 multi-version snapshots. When working inside a project that depends on NutUI, prefer **not** passing `--nutui-version` — let the CLI infer it from the project's `node_modules` / `package.json`. Only pass `--nv ` when the user explicitly asks about a specific version or you are outside a project context. The `_meta.version` / `_meta.source` in the output tells you which version was actually resolved. diff --git a/packages/nutui-react-taro-cli/skills/nutui-react-taro/SKILL.md b/packages/nutui-react-taro-cli/skills/nutui-react-taro/SKILL.md index 66370e1540..d55a06aa4c 100644 --- a/packages/nutui-react-taro-cli/skills/nutui-react-taro/SKILL.md +++ b/packages/nutui-react-taro-cli/skills/nutui-react-taro/SKILL.md @@ -118,8 +118,9 @@ nutui-react-taro token Button --format json | 参数 | 用途 | | --- | --- | | `--format, -f ` | 输出格式;agent 应优先使用 `json`(默认:`text`) | +| `--nutui-version, --nv ` | 目标 NutUI 版本(如 `3`、`3.1.0`、`4.0.0-beta.7`);省略则自动检测 | | `--help, -h` | 显示帮助 | -| `--version, -v` | 打印 CLI 版本 | +| `--version, -v` | 打印 CLI 自身版本(不是 NutUI 版本) | ## 核心规则 @@ -128,3 +129,4 @@ nutui-react-taro token Button --format json 3. **确认组件存在** —— 如果不确定确切名称,运行 `nutui-react-taro list`,或借助 CLI 的「你是不是想找」建议,而不是导入一个猜测的名称。 4. **使用 Design Token 做样式** —— NutUI 采用 `nut-` 扁平 BEM 类名和 `var(--nutui-*)` 变量体系。定制外观时,查询 `nutui-react-taro token` 并使用 Token,而不是硬编码颜色或间距。 5. **从 `@nutui/nutui-react-taro` 导入** —— 不是 `@nutui/nutui-react`。这是 Taro(小程序 / 跨端)包。 +6. **版本优先自动检测** —— CLI 内置 v3 / v4 多版本快照。在有 NutUI 依赖的项目里工作时,优先**不传** `--nutui-version`,让 CLI 从项目 `node_modules` / `package.json` 自动推断;只有当用户明确要查某个特定版本、或不在项目上下文中时,才显式传 `--nv `。输出里的 `_meta.version` / `_meta.source` 会告诉你实际命中的版本。 diff --git a/packages/nutui-react-taro-cli/src/cli.ts b/packages/nutui-react-taro-cli/src/cli.ts index 819d920856..e2051e823e 100644 --- a/packages/nutui-react-taro-cli/src/cli.ts +++ b/packages/nutui-react-taro-cli/src/cli.ts @@ -12,6 +12,7 @@ const dataDir = fileURLToPath(new URL('../data/', import.meta.url)) const config: CliConfig = { binName: 'nutui-react-taro', cliPkgName: '@nutui/nutui-react-taro-cli', + npmPackageName: '@nutui/nutui-react-taro', platform: 'taro', libLabel: 'NutUI React(Taro 多端)', demoLabel: 'Taro', diff --git a/packages/nutui-react-taro-cli/tsup.config.ts b/packages/nutui-react-taro-cli/tsup.config.ts index 9a2ad73922..f57de67ec0 100644 --- a/packages/nutui-react-taro-cli/tsup.config.ts +++ b/packages/nutui-react-taro-cli/tsup.config.ts @@ -14,6 +14,7 @@ export default defineConfig({ // core 为 workspace 内 private 包(不发布),必须一并 bundle 进产物。 noExternal: [ 'yargs', + 'semver', '@modelcontextprotocol/sdk', '@nutui/nutui-react-cli-core', ], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7ce68b0d97..0ab5d733d8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -95,7 +95,7 @@ importers: version: 3.1.1(rollup@4.63.1) '@pmmmwh/react-refresh-webpack-plugin': specifier: 0.6.2 - version: 0.6.2(react-refresh@0.18.0)(type-fest@4.41.0)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + version: 0.6.2(react-refresh@0.18.0)(type-fest@4.41.0)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) '@rollup/plugin-babel': specifier: ^6.1.0 version: 6.1.0(@babel/core@7.29.7)(@types/babel__core@7.20.5)(rollup@4.63.1) @@ -116,7 +116,7 @@ importers: version: 1.16.1(@swc/helpers@0.5.23) '@tarojs/components': specifier: 4.2.0 - version: 4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@4.63.1)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + version: 4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@4.63.1)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) '@tarojs/plugin-platform-alipay': specifier: 4.2.0 version: 4.2.0(@tarojs/service@4.2.0(@swc/helpers@0.5.23))(@tarojs/shared@4.2.0) @@ -131,7 +131,7 @@ importers: version: 4.2.0(react@18.3.1) '@tarojs/taro': specifier: 4.2.0 - version: 4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@4.63.1)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@tarojs/shared@4.2.0)(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@4.63.1)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + version: 4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@4.63.1)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@tarojs/shared@4.2.0)(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@4.63.1)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) '@testing-library/jest-dom': specifier: ^6.9.1 version: 6.9.1 @@ -188,7 +188,7 @@ importers: version: 2.1.9(vitest@3.2.7) autoprefixer: specifier: ^10.5.4 - version: 10.5.4(postcss@8.5.26) + version: 10.5.4(postcss@8.5.28) axios: specifier: ^1.19.0 version: 1.20.0(debug@4.4.3) @@ -242,7 +242,7 @@ importers: version: 0.5.0 gulp-postcss: specifier: ^10.0.0 - version: 10.0.0(jiti@2.7.0)(postcss@8.5.26)(tsx@4.23.13) + version: 10.0.0(jiti@2.7.0)(postcss@8.5.28)(tsx@4.23.13) gulp-rename: specifier: ^2.1.0 version: 2.1.0 @@ -287,22 +287,22 @@ importers: version: 4.1.1(mobx@6.16.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) postcss: specifier: ^8.5.26 - version: 8.5.26 + version: 8.5.28 postcss-css-variables: specifier: ^0.19.0 - version: 0.19.0(postcss@8.5.26) + version: 0.19.0(postcss@8.5.28) postcss-import: specifier: ^16.2.0 - version: 16.2.0(postcss@8.5.26) + version: 16.2.0(postcss@8.5.28) postcss-modules: specifier: ^6.0.1 - version: 6.0.1(postcss@8.5.26) + version: 6.0.1(postcss@8.5.28) postcss-rtlcss: specifier: ^5.7.1 - version: 5.7.1(postcss@8.5.26) + version: 5.7.1(postcss@8.5.28) postcss-scss: specifier: ^4.0.9 - version: 4.0.9(postcss@8.5.26) + version: 4.0.9(postcss@8.5.28) prettier: specifier: ^3.9.6 version: 3.9.6 @@ -447,7 +447,7 @@ importers: version: 3.9.6 tsup: specifier: ^8.5.1 - version: 8.5.1(@microsoft/api-extractor@7.59.0(@types/node@22.20.1))(@swc/core@1.16.1(@swc/helpers@0.5.23))(jiti@2.7.0)(postcss@8.5.26)(tsx@4.23.13)(typescript@5.9.3)(yaml@2.9.0) + version: 8.5.1(@microsoft/api-extractor@7.59.0(@types/node@22.20.1))(@swc/core@1.16.1(@swc/helpers@0.5.23))(jiti@2.7.0)(postcss@8.5.28)(tsx@4.23.13)(typescript@5.9.3)(yaml@2.9.0) tsx: specifier: ^4.23.12 version: 4.23.13 @@ -475,16 +475,16 @@ importers: devDependencies: jest: specifier: ^30.4.2 - version: 30.4.2(@types/node@20.19.43) + version: 30.4.2(@types/node@22.20.1) prettier: specifier: ^3.9.6 version: 3.9.6 release-it: specifier: ^18.1.2 - version: 18.1.2(@types/node@20.19.43)(typescript@5.9.3) + version: 18.1.2(@types/node@22.20.1)(typescript@5.9.3) ts-jest: specifier: ^29.4.12 - version: 29.4.12(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(jest-util@30.4.1)(jest@30.4.2(@types/node@20.19.43))(typescript@5.9.3) + version: 29.4.12(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(jest-util@30.4.1)(jest@30.4.2(@types/node@22.20.1))(typescript@5.9.3) packages/nutui-react-cli: dependencies: @@ -506,7 +506,7 @@ importers: version: 9.11.1(magicast@0.3.5) tsup: specifier: ^8.5.1 - version: 8.5.1(@microsoft/api-extractor@7.59.0(@types/node@22.20.1))(@swc/core@1.16.1(@swc/helpers@0.5.23))(jiti@2.7.0)(postcss@8.5.26)(tsx@4.23.13)(typescript@5.9.3)(yaml@2.9.0) + version: 8.5.1(@microsoft/api-extractor@7.59.0(@types/node@22.20.1))(@swc/core@1.16.1(@swc/helpers@0.5.23))(jiti@2.7.0)(postcss@8.5.28)(tsx@4.23.13)(typescript@5.9.3)(yaml@2.9.0) typescript: specifier: ^5.6.2 version: 5.9.3 @@ -516,6 +516,9 @@ importers: '@modelcontextprotocol/sdk': specifier: ^1.30.0 version: 1.30.0(zod@4.4.3) + semver: + specifier: ^7.8.0 + version: 7.8.1 yargs: specifier: ^17.7.3 version: 17.7.3 @@ -523,12 +526,21 @@ importers: '@types/node': specifier: ^22.20.1 version: 22.20.1 + '@types/semver': + specifier: ^7.7.1 + version: 7.7.1 '@types/yargs': specifier: ^17.0.35 version: 17.0.35 + markdown-it: + specifier: ^14.2.0 + version: 14.3.1 typescript: specifier: ^5.6.2 version: 5.9.3 + vitest: + specifier: ^3.2.7 + version: 3.2.7(@types/debug@4.1.13)(@types/node@22.20.1)(@vitest/ui@2.1.9)(happy-dom@14.12.3)(jsdom@24.1.3)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0) packages/nutui-react-taro-cli: dependencies: @@ -550,7 +562,7 @@ importers: version: 9.11.1(magicast@0.3.5) tsup: specifier: ^8.5.1 - version: 8.5.1(@microsoft/api-extractor@7.59.0(@types/node@22.20.1))(@swc/core@1.16.1(@swc/helpers@0.5.23))(jiti@2.7.0)(postcss@8.5.26)(tsx@4.23.13)(typescript@5.9.3)(yaml@2.9.0) + version: 8.5.1(@microsoft/api-extractor@7.59.0(@types/node@22.20.1))(@swc/core@1.16.1(@swc/helpers@0.5.23))(jiti@2.7.0)(postcss@8.5.28)(tsx@4.23.13)(typescript@5.9.3)(yaml@2.9.0) typescript: specifier: ^5.6.2 version: 5.9.3 @@ -580,7 +592,7 @@ importers: version: 7.27.0 vitest: specifier: ^1.6.1 - version: 1.6.1(@types/node@18.19.130)(@vitest/ui@2.1.9(vitest@3.2.7))(happy-dom@14.12.3)(jsdom@24.1.3)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0) + version: 1.6.1(@types/node@18.19.130)(@vitest/ui@2.1.9)(happy-dom@14.12.3)(jsdom@24.1.3)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0) devDependencies: '@types/lodash.kebabcase': specifier: ^4.1.9 @@ -611,22 +623,22 @@ importers: version: 1.0.2 '@nutui/replace-icons': specifier: ^1.0.3 - version: 1.0.3(@types/node@22.20.1)(@vitest/ui@2.1.9(vitest@3.2.7))(happy-dom@14.12.3)(jsdom@24.1.3)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0) + version: 1.0.3(@types/node@22.20.1)(@vitest/ui@2.1.9)(happy-dom@14.12.3)(jsdom@24.1.3)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0) '@nutui/touch-emulator': specifier: ^1.0.0 version: 1.0.0 '@pmmmwh/react-refresh-webpack-plugin': specifier: ^0.6.2 - version: 0.6.2(react-refresh@0.18.0)(type-fest@4.41.0)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + version: 0.6.2(react-refresh@0.18.0)(type-fest@4.41.0)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) '@tarojs/components': specifier: 4.2.0 - version: 4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + version: 4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) '@tarojs/helper': specifier: 4.1.3 version: 4.1.3(@swc/helpers@0.5.23) '@tarojs/plugin-framework-react': specifier: 4.1.3 - version: 4.1.3(@pmmmwh/react-refresh-webpack-plugin@0.6.2(react-refresh@0.18.0)(type-fest@4.41.0)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@tarojs/runtime@4.1.3)(@tarojs/shared@4.1.3)(@vitejs/plugin-react@4.7.0(vite@5.4.21(@types/node@22.20.1)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0)))(react@18.3.1)(vite@5.4.21(@types/node@22.20.1)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + version: 4.1.3(@pmmmwh/react-refresh-webpack-plugin@0.6.2(react-refresh@0.18.0)(type-fest@4.41.0)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@tarojs/runtime@4.1.3)(@tarojs/shared@4.1.3)(@vitejs/plugin-react@4.7.0(vite@5.4.21(@types/node@22.20.1)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0)))(react@18.3.1)(vite@5.4.21(@types/node@22.20.1)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) '@tarojs/plugin-html': specifier: 4.1.3 version: 4.1.3(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@tarojs/runtime@4.1.3)(@tarojs/shared@4.1.3) @@ -635,7 +647,7 @@ importers: version: 4.1.3(@tarojs/service@4.1.3(@swc/helpers@0.5.23))(@tarojs/shared@4.1.3) '@tarojs/plugin-platform-h5': specifier: 4.1.3 - version: 4.1.3(c3fd5ae994df330e83f055407979cfa2) + version: 4.1.3(e48d17c9af345a333d434f02d921caa5) '@tarojs/plugin-platform-jd': specifier: 4.1.3 version: 4.1.3(@tarojs/service@4.1.3(@swc/helpers@0.5.23))(@tarojs/shared@4.1.3) @@ -656,7 +668,7 @@ importers: version: 4.1.3(react@18.3.1) '@tarojs/router': specifier: 4.1.3 - version: 4.1.3(afb1c0d56d64e98a918e5783765a535b) + version: 4.1.3(4802cbeebfe1a66f5f36155868e0a2b1) '@tarojs/runtime': specifier: 4.1.3 version: 4.1.3 @@ -665,10 +677,10 @@ importers: version: 4.1.3 '@tarojs/taro': specifier: 4.1.3 - version: 4.1.3(@tarojs/components@4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@tarojs/shared@4.1.3)(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + version: 4.1.3(@tarojs/components@4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@tarojs/shared@4.1.3)(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) '@tarojs/taro-h5': specifier: 4.1.3 - version: 4.1.3(ac72ca359558e5bd4d85497634a73199) + version: 4.1.3(09f260d77208d3a0f8c7621b42b3d2e1) babel-plugin-import: specifier: ^1.13.8 version: 1.13.8 @@ -702,13 +714,13 @@ importers: version: 4.1.3(@swc/helpers@0.5.23)(@types/node@22.20.1) '@tarojs/plugin-platform-harmony-ets': specifier: 4.1.3 - version: 4.1.3(@babel/core@7.29.7)(@swc/core@1.3.96(@swc/helpers@0.5.23))(@swc/helpers@0.5.23)(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(lightningcss@1.32.0)(postcss@8.5.26)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26))) + version: 4.1.3(@babel/core@7.29.7)(@swc/core@1.3.96(@swc/helpers@0.5.23))(@swc/helpers@0.5.23)(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(lightningcss@1.32.0)(postcss@8.5.28)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28))) '@tarojs/vite-runner': specifier: 4.1.3 - version: 4.1.3(@swc/helpers@0.5.23)(@tarojs/runtime@4.1.3)(@types/babel__core@7.20.5)(jiti@2.7.0)(postcss@8.5.26)(rollup@3.30.0)(terser@5.48.0)(tsx@4.23.13)(typescript@5.9.3)(vite@5.4.21(@types/node@22.20.1)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0)) + version: 4.1.3(@swc/helpers@0.5.23)(@tarojs/runtime@4.1.3)(@types/babel__core@7.20.5)(jiti@2.7.0)(postcss@8.5.28)(rollup@3.30.0)(terser@5.48.0)(tsx@4.23.13)(typescript@5.9.3)(vite@5.4.21(@types/node@22.20.1)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0)) '@tarojs/webpack5-runner': specifier: 4.1.3 - version: 4.1.3(@babel/core@7.29.7)(@swc/core@1.3.96(@swc/helpers@0.5.23))(@swc/helpers@0.5.23)(@tarojs/runtime@4.1.3)(less@3.13.1)(postcss@8.5.26)(sass@1.103.1)(stylus@0.64.0)(typescript@5.9.3)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + version: 4.1.3(@babel/core@7.29.7)(@swc/core@1.3.96(@swc/helpers@0.5.23))(@swc/helpers@0.5.23)(@tarojs/runtime@4.1.3)(less@3.13.1)(postcss@8.5.28)(sass@1.103.1)(stylus@0.64.0)(typescript@5.9.3)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) '@types/react': specifier: ^18.3.31 version: 18.3.31 @@ -756,7 +768,7 @@ importers: version: 4.6.2(eslint@8.57.1) postcss: specifier: ^8.5.26 - version: 8.5.26 + version: 8.5.28 stylelint: specifier: ^16.26.1 version: 16.26.1(typescript@5.9.3) @@ -7889,11 +7901,12 @@ packages: glob@11.1.0: resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==} engines: {node: 20 || >=22} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me global-directory@4.0.1: resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} @@ -11050,8 +11063,8 @@ packages: resolution: {integrity: sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==} engines: {node: '>=6.0.0'} - postcss@8.5.26: - resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==} + postcss@8.5.28: + resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: @@ -12498,6 +12511,7 @@ packages: tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me teex@1.0.1: resolution: {integrity: sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==} @@ -13118,6 +13132,7 @@ packages: uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true v8-to-istanbul@9.3.0: @@ -15253,6 +15268,16 @@ snapshots: optionalDependencies: '@types/node': 20.19.43 + '@inquirer/checkbox@4.3.2(@types/node@22.20.1)': + dependencies: + '@inquirer/ansi': 1.0.2 + '@inquirer/core': 10.3.2(@types/node@22.20.1) + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@22.20.1) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 22.20.1 + '@inquirer/confirm@5.1.21(@types/node@20.19.43)': dependencies: '@inquirer/core': 10.3.2(@types/node@20.19.43) @@ -15260,6 +15285,13 @@ snapshots: optionalDependencies: '@types/node': 20.19.43 + '@inquirer/confirm@5.1.21(@types/node@22.20.1)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@22.20.1) + '@inquirer/type': 3.0.10(@types/node@22.20.1) + optionalDependencies: + '@types/node': 22.20.1 + '@inquirer/core@10.3.2(@types/node@20.19.43)': dependencies: '@inquirer/ansi': 1.0.2 @@ -15273,6 +15305,19 @@ snapshots: optionalDependencies: '@types/node': 20.19.43 + '@inquirer/core@10.3.2(@types/node@22.20.1)': + dependencies: + '@inquirer/ansi': 1.0.2 + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@22.20.1) + cli-width: 4.1.0 + mute-stream: 2.0.0 + signal-exit: 4.1.0 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 22.20.1 + '@inquirer/editor@4.2.23(@types/node@20.19.43)': dependencies: '@inquirer/core': 10.3.2(@types/node@20.19.43) @@ -15281,6 +15326,14 @@ snapshots: optionalDependencies: '@types/node': 20.19.43 + '@inquirer/editor@4.2.23(@types/node@22.20.1)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@22.20.1) + '@inquirer/external-editor': 1.0.3(@types/node@22.20.1) + '@inquirer/type': 3.0.10(@types/node@22.20.1) + optionalDependencies: + '@types/node': 22.20.1 + '@inquirer/expand@4.0.23(@types/node@20.19.43)': dependencies: '@inquirer/core': 10.3.2(@types/node@20.19.43) @@ -15289,6 +15342,14 @@ snapshots: optionalDependencies: '@types/node': 20.19.43 + '@inquirer/expand@4.0.23(@types/node@22.20.1)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@22.20.1) + '@inquirer/type': 3.0.10(@types/node@22.20.1) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 22.20.1 + '@inquirer/external-editor@1.0.3(@types/node@20.19.43)': dependencies: chardet: 2.1.1 @@ -15312,6 +15373,13 @@ snapshots: optionalDependencies: '@types/node': 20.19.43 + '@inquirer/input@4.3.1(@types/node@22.20.1)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@22.20.1) + '@inquirer/type': 3.0.10(@types/node@22.20.1) + optionalDependencies: + '@types/node': 22.20.1 + '@inquirer/number@3.0.23(@types/node@20.19.43)': dependencies: '@inquirer/core': 10.3.2(@types/node@20.19.43) @@ -15319,6 +15387,13 @@ snapshots: optionalDependencies: '@types/node': 20.19.43 + '@inquirer/number@3.0.23(@types/node@22.20.1)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@22.20.1) + '@inquirer/type': 3.0.10(@types/node@22.20.1) + optionalDependencies: + '@types/node': 22.20.1 + '@inquirer/password@4.0.23(@types/node@20.19.43)': dependencies: '@inquirer/ansi': 1.0.2 @@ -15327,6 +15402,14 @@ snapshots: optionalDependencies: '@types/node': 20.19.43 + '@inquirer/password@4.0.23(@types/node@22.20.1)': + dependencies: + '@inquirer/ansi': 1.0.2 + '@inquirer/core': 10.3.2(@types/node@22.20.1) + '@inquirer/type': 3.0.10(@types/node@22.20.1) + optionalDependencies: + '@types/node': 22.20.1 + '@inquirer/prompts@7.10.1(@types/node@20.19.43)': dependencies: '@inquirer/checkbox': 4.3.2(@types/node@20.19.43) @@ -15342,6 +15425,21 @@ snapshots: optionalDependencies: '@types/node': 20.19.43 + '@inquirer/prompts@7.10.1(@types/node@22.20.1)': + dependencies: + '@inquirer/checkbox': 4.3.2(@types/node@22.20.1) + '@inquirer/confirm': 5.1.21(@types/node@22.20.1) + '@inquirer/editor': 4.2.23(@types/node@22.20.1) + '@inquirer/expand': 4.0.23(@types/node@22.20.1) + '@inquirer/input': 4.3.1(@types/node@22.20.1) + '@inquirer/number': 3.0.23(@types/node@22.20.1) + '@inquirer/password': 4.0.23(@types/node@22.20.1) + '@inquirer/rawlist': 4.1.11(@types/node@22.20.1) + '@inquirer/search': 3.2.2(@types/node@22.20.1) + '@inquirer/select': 4.4.2(@types/node@22.20.1) + optionalDependencies: + '@types/node': 22.20.1 + '@inquirer/rawlist@4.1.11(@types/node@20.19.43)': dependencies: '@inquirer/core': 10.3.2(@types/node@20.19.43) @@ -15350,6 +15448,14 @@ snapshots: optionalDependencies: '@types/node': 20.19.43 + '@inquirer/rawlist@4.1.11(@types/node@22.20.1)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@22.20.1) + '@inquirer/type': 3.0.10(@types/node@22.20.1) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 22.20.1 + '@inquirer/search@3.2.2(@types/node@20.19.43)': dependencies: '@inquirer/core': 10.3.2(@types/node@20.19.43) @@ -15359,6 +15465,15 @@ snapshots: optionalDependencies: '@types/node': 20.19.43 + '@inquirer/search@3.2.2(@types/node@22.20.1)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@22.20.1) + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@22.20.1) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 22.20.1 + '@inquirer/select@4.4.2(@types/node@20.19.43)': dependencies: '@inquirer/ansi': 1.0.2 @@ -15369,10 +15484,24 @@ snapshots: optionalDependencies: '@types/node': 20.19.43 + '@inquirer/select@4.4.2(@types/node@22.20.1)': + dependencies: + '@inquirer/ansi': 1.0.2 + '@inquirer/core': 10.3.2(@types/node@22.20.1) + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@22.20.1) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 22.20.1 + '@inquirer/type@3.0.10(@types/node@20.19.43)': optionalDependencies: '@types/node': 20.19.43 + '@inquirer/type@3.0.10(@types/node@22.20.1)': + optionalDependencies: + '@types/node': 22.20.1 + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -15842,7 +15971,7 @@ snapshots: transitivePeerDependencies: - react-dom - '@nutui/replace-icons@1.0.3(@types/node@22.20.1)(@vitest/ui@2.1.9(vitest@3.2.7))(happy-dom@14.12.3)(jsdom@24.1.3)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0)': + '@nutui/replace-icons@1.0.3(@types/node@22.20.1)(@vitest/ui@2.1.9)(happy-dom@14.12.3)(jsdom@24.1.3)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0)': dependencies: '@babel/cli': 7.29.7(@babel/core@7.29.7) '@babel/core': 7.29.7 @@ -15851,7 +15980,7 @@ snapshots: '@babel/preset-react': 7.29.7(@babel/core@7.29.7) '@types/babel__core': 7.20.5 '@types/babel__generator': 7.27.0 - vitest: 1.6.1(@types/node@22.20.1)(@vitest/ui@2.1.9(vitest@3.2.7))(happy-dom@14.12.3)(jsdom@24.1.3)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0) + vitest: 1.6.1(@types/node@22.20.1)(@vitest/ui@2.1.9)(happy-dom@14.12.3)(jsdom@24.1.3)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0) transitivePeerDependencies: - '@edge-runtime/vm' - '@types/node' @@ -16070,7 +16199,7 @@ snapshots: '@pkgr/core@0.3.6': {} - '@pmmmwh/react-refresh-webpack-plugin@0.6.2(react-refresh@0.18.0)(type-fest@4.41.0)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26))': + '@pmmmwh/react-refresh-webpack-plugin@0.6.2(react-refresh@0.18.0)(type-fest@4.41.0)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28))': dependencies: anser: 2.3.5 core-js-pure: 3.49.0 @@ -16079,12 +16208,12 @@ snapshots: react-refresh: 0.18.0 schema-utils: 4.3.3 source-map: 0.7.6 - webpack: 5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) optionalDependencies: type-fest: 4.41.0 - webpack-dev-server: 4.15.2(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + webpack-dev-server: 4.15.2(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) - '@pmmmwh/react-refresh-webpack-plugin@0.6.2(react-refresh@0.18.0)(type-fest@4.41.0)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26))': + '@pmmmwh/react-refresh-webpack-plugin@0.6.2(react-refresh@0.18.0)(type-fest@4.41.0)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28))': dependencies: anser: 2.3.5 core-js-pure: 3.49.0 @@ -16093,10 +16222,10 @@ snapshots: react-refresh: 0.18.0 schema-utils: 4.3.3 source-map: 0.7.6 - webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) optionalDependencies: type-fest: 4.41.0 - webpack-dev-server: 4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + webpack-dev-server: 4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) '@pnpm/config.env-replace@1.1.0': {} @@ -16731,12 +16860,12 @@ snapshots: - debug - supports-color - '@tarojs/components-react@4.1.3(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(react@18.3.1)(rollup@3.30.0)(solid-js@1.9.13)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26))': + '@tarojs/components-react@4.1.3(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(react@18.3.1)(rollup@3.30.0)(solid-js@1.9.13)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28))': dependencies: '@babel/runtime': 7.29.7 - '@tarojs/components': 4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + '@tarojs/components': 4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) '@tarojs/shared': 4.1.3 - '@tarojs/taro': 4.1.3(@tarojs/components@4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@tarojs/shared@4.1.3)(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + '@tarojs/taro': 4.1.3(@tarojs/components@4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@tarojs/shared@4.1.3)(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) classnames: 2.5.1 react: 18.3.1 solid-js: 1.9.13 @@ -16753,12 +16882,12 @@ snapshots: - webpack-chain - webpack-dev-server - '@tarojs/components@4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26))': + '@tarojs/components@4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28))': dependencies: '@stencil/core': 2.22.3 '@tarojs/runtime': 4.2.0 '@tarojs/shared': 4.2.0 - '@tarojs/taro': 4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@tarojs/shared@4.2.0)(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + '@tarojs/taro': 4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@tarojs/shared@4.2.0)(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) classnames: 2.5.1 hammerjs: 2.0.8 hls.js: 1.6.16 @@ -16776,12 +16905,12 @@ snapshots: - webpack-chain - webpack-dev-server - '@tarojs/components@4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@4.63.1)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26))': + '@tarojs/components@4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@4.63.1)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28))': dependencies: '@stencil/core': 2.22.3 '@tarojs/runtime': 4.2.0 '@tarojs/shared': 4.2.0 - '@tarojs/taro': 4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@4.63.1)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@tarojs/shared@4.2.0)(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@4.63.1)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + '@tarojs/taro': 4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@4.63.1)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@tarojs/shared@4.2.0)(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@4.63.1)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) classnames: 2.5.1 hammerjs: 2.0.8 hls.js: 1.6.16 @@ -16830,9 +16959,9 @@ snapshots: dependencies: '@babel/core': 7.29.7 '@babel/generator': 7.29.8 - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.8 '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@swc/core': 1.3.96(@swc/helpers@0.5.23) '@swc/register': 0.1.10(@swc/core@1.3.96(@swc/helpers@0.5.23)) ansi-escapes: 4.3.2 @@ -16948,7 +17077,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@tarojs/plugin-framework-react@4.1.3(@pmmmwh/react-refresh-webpack-plugin@0.6.2(react-refresh@0.18.0)(type-fest@4.41.0)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@tarojs/runtime@4.1.3)(@tarojs/shared@4.1.3)(@vitejs/plugin-react@4.7.0(vite@5.4.21(@types/node@22.20.1)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0)))(react@18.3.1)(vite@5.4.21(@types/node@22.20.1)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26))': + '@tarojs/plugin-framework-react@4.1.3(@pmmmwh/react-refresh-webpack-plugin@0.6.2(react-refresh@0.18.0)(type-fest@4.41.0)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@tarojs/runtime@4.1.3)(@tarojs/shared@4.1.3)(@vitejs/plugin-react@4.7.0(vite@5.4.21(@types/node@22.20.1)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0)))(react@18.3.1)(vite@5.4.21(@types/node@22.20.1)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28))': dependencies: '@tarojs/helper': 4.1.3(@swc/helpers@0.5.23) '@tarojs/runtime': 4.1.3 @@ -16958,11 +17087,11 @@ snapshots: lodash: 4.18.1 tslib: 2.8.1 optionalDependencies: - '@pmmmwh/react-refresh-webpack-plugin': 0.6.2(react-refresh@0.18.0)(type-fest@4.41.0)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + '@pmmmwh/react-refresh-webpack-plugin': 0.6.2(react-refresh@0.18.0)(type-fest@4.41.0)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) '@vitejs/plugin-react': 4.7.0(vite@5.4.21(@types/node@22.20.1)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0)) react: 18.3.1 vite: 5.4.21(@types/node@22.20.1)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0) - webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) '@tarojs/plugin-html@4.1.3(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@tarojs/runtime@4.1.3)(@tarojs/shared@4.1.3)': dependencies: @@ -16980,16 +17109,16 @@ snapshots: '@tarojs/service': 4.2.0(@swc/helpers@0.5.23) '@tarojs/shared': 4.2.0 - '@tarojs/plugin-platform-h5@4.1.3(c3fd5ae994df330e83f055407979cfa2)': + '@tarojs/plugin-platform-h5@4.1.3(e48d17c9af345a333d434f02d921caa5)': dependencies: '@babel/core': 7.29.7 - '@tarojs/components': 4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) - '@tarojs/components-react': 4.1.3(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(react@18.3.1)(rollup@3.30.0)(solid-js@1.9.13)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + '@tarojs/components': 4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) + '@tarojs/components-react': 4.1.3(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(react@18.3.1)(rollup@3.30.0)(solid-js@1.9.13)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) '@tarojs/helper': 4.1.3(@swc/helpers@0.5.23) '@tarojs/runtime': 4.1.3 '@tarojs/service': 4.1.3(@swc/helpers@0.5.23) '@tarojs/shared': 4.1.3 - '@tarojs/taro-h5': 4.1.3(ac72ca359558e5bd4d85497634a73199) + '@tarojs/taro-h5': 4.1.3(09f260d77208d3a0f8c7621b42b3d2e1) babel-plugin-transform-taroapi: 4.1.3(@babel/core@7.29.7) change-case: 4.1.2 lodash-es: 4.17.21 @@ -17009,17 +17138,17 @@ snapshots: - webpack-chain - webpack-dev-server - '@tarojs/plugin-platform-harmony-ets@4.1.3(@babel/core@7.29.7)(@swc/core@1.3.96(@swc/helpers@0.5.23))(@swc/helpers@0.5.23)(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(lightningcss@1.32.0)(postcss@8.5.26)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))': + '@tarojs/plugin-platform-harmony-ets@4.1.3(@babel/core@7.29.7)(@swc/core@1.3.96(@swc/helpers@0.5.23))(@swc/helpers@0.5.23)(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(lightningcss@1.32.0)(postcss@8.5.28)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))': dependencies: '@babel/preset-react': 7.29.7(@babel/core@7.29.7) - '@tarojs/components': 4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + '@tarojs/components': 4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) '@tarojs/helper': 4.1.3(@swc/helpers@0.5.23) '@tarojs/runner-utils': 4.1.3(@swc/helpers@0.5.23) '@tarojs/runtime': 4.1.3 '@tarojs/service': 4.1.3(@swc/helpers@0.5.23) '@tarojs/shared': 4.1.3 - '@tarojs/taro': 4.1.3(@tarojs/components@4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@tarojs/shared@4.1.3)(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) - webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + '@tarojs/taro': 4.1.3(@tarojs/components@4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@tarojs/shared@4.1.3)(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) + webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) webpack-sources: 3.5.0 transitivePeerDependencies: - '@babel/core' @@ -17095,11 +17224,11 @@ snapshots: react: 18.3.1 react-reconciler: 0.29.0(react@18.3.1) - '@tarojs/router@4.1.3(afb1c0d56d64e98a918e5783765a535b)': + '@tarojs/router@4.1.3(4802cbeebfe1a66f5f36155868e0a2b1)': dependencies: '@tarojs/runtime': 4.1.3 '@tarojs/shared': 4.1.3 - '@tarojs/taro': 4.1.3(@tarojs/components@4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@tarojs/shared@4.1.3)(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + '@tarojs/taro': 4.1.3(@tarojs/components@4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@tarojs/shared@4.1.3)(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) dingtalk-jsapi: 2.15.6 history: 5.3.0 mobile-detect: 1.4.5 @@ -17169,11 +17298,11 @@ snapshots: '@tarojs/shared@4.2.0': {} - '@tarojs/taro-h5@4.1.3(ac72ca359558e5bd4d85497634a73199)': + '@tarojs/taro-h5@4.1.3(09f260d77208d3a0f8c7621b42b3d2e1)': dependencies: '@tarojs/api': 4.2.0(@tarojs/runtime@4.1.3)(@tarojs/shared@4.1.3) - '@tarojs/components': 4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) - '@tarojs/router': 4.1.3(afb1c0d56d64e98a918e5783765a535b) + '@tarojs/components': 4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) + '@tarojs/router': 4.1.3(4802cbeebfe1a66f5f36155868e0a2b1) '@tarojs/runtime': 4.1.3 '@tarojs/shared': 4.1.3 abortcontroller-polyfill: 1.7.8 @@ -17189,67 +17318,67 @@ snapshots: transitivePeerDependencies: - '@tarojs/taro' - '@tarojs/taro-loader@4.1.3(@swc/helpers@0.5.23)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26))': + '@tarojs/taro-loader@4.1.3(@swc/helpers@0.5.23)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28))': dependencies: '@tarojs/helper': 4.1.3(@swc/helpers@0.5.23) '@tarojs/shared': 4.1.3 - webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) transitivePeerDependencies: - '@swc/helpers' - supports-color - '@tarojs/taro@4.1.3(@tarojs/components@4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@tarojs/shared@4.1.3)(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26))': + '@tarojs/taro@4.1.3(@tarojs/components@4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@tarojs/shared@4.1.3)(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28))': dependencies: '@tarojs/api': 4.2.0(@tarojs/runtime@4.1.3)(@tarojs/shared@4.1.3) - '@tarojs/components': 4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + '@tarojs/components': 4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) '@tarojs/helper': 4.1.3(@swc/helpers@0.5.23) '@tarojs/runtime': 4.1.3 '@tarojs/shared': 4.1.3 '@types/postcss-url': 10.0.4 - postcss: 8.5.26 + postcss: 8.5.28 optionalDependencies: '@types/react': 18.3.31 - html-webpack-plugin: 5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + html-webpack-plugin: 5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) rollup: 3.30.0 - webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) webpack-chain: 6.5.1 - webpack-dev-server: 4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + webpack-dev-server: 4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) - '@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@tarojs/shared@4.2.0)(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26))': + '@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@tarojs/shared@4.2.0)(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28))': dependencies: '@tarojs/api': 4.2.0(@tarojs/runtime@4.2.0)(@tarojs/shared@4.2.0) - '@tarojs/components': 4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + '@tarojs/components': 4.2.0(@tarojs/helper@4.1.3(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@3.30.0)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) '@tarojs/helper': 4.1.3(@swc/helpers@0.5.23) '@tarojs/runtime': 4.2.0 '@tarojs/shared': 4.2.0 '@types/postcss-url': 10.0.4 - postcss: 8.5.26 + postcss: 8.5.28 optionalDependencies: '@types/react': 18.3.31 - html-webpack-plugin: 5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + html-webpack-plugin: 5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) rollup: 3.30.0 - webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) webpack-chain: 6.5.1 - webpack-dev-server: 4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + webpack-dev-server: 4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) - '@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@4.63.1)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@tarojs/shared@4.2.0)(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@4.63.1)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26))': + '@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@4.63.1)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@tarojs/shared@4.2.0)(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@4.63.1)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28))': dependencies: '@tarojs/api': 4.2.0(@tarojs/runtime@4.2.0)(@tarojs/shared@4.2.0) - '@tarojs/components': 4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(postcss@8.5.26)(rollup@4.63.1)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)))(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + '@tarojs/components': 4.2.0(@tarojs/helper@4.2.0(@swc/helpers@0.5.23))(@types/react@18.3.31)(html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(postcss@8.5.28)(rollup@4.63.1)(webpack-chain@6.5.1)(webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)))(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) '@tarojs/helper': 4.2.0(@swc/helpers@0.5.23) '@tarojs/runtime': 4.2.0 '@tarojs/shared': 4.2.0 '@types/postcss-url': 10.0.4 - postcss: 8.5.26 + postcss: 8.5.28 optionalDependencies: '@types/react': 18.3.31 - html-webpack-plugin: 5.6.7(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + html-webpack-plugin: 5.6.7(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) rollup: 4.63.1 - webpack: 5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) webpack-chain: 6.5.1 - webpack-dev-server: 4.15.2(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + webpack-dev-server: 4.15.2(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) - '@tarojs/vite-runner@4.1.3(@swc/helpers@0.5.23)(@tarojs/runtime@4.1.3)(@types/babel__core@7.20.5)(jiti@2.7.0)(postcss@8.5.26)(rollup@3.30.0)(terser@5.48.0)(tsx@4.23.13)(typescript@5.9.3)(vite@5.4.21(@types/node@22.20.1)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0))': + '@tarojs/vite-runner@4.1.3(@swc/helpers@0.5.23)(@tarojs/runtime@4.1.3)(@types/babel__core@7.20.5)(jiti@2.7.0)(postcss@8.5.28)(rollup@3.30.0)(terser@5.48.0)(tsx@4.23.13)(typescript@5.9.3)(vite@5.4.21(@types/node@22.20.1)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0))': dependencies: '@ampproject/remapping': 2.3.0 '@babel/core': 7.29.7 @@ -17264,7 +17393,7 @@ snapshots: '@tarojs/shared': 4.1.3 '@vitejs/plugin-legacy': 4.1.1(terser@5.48.0)(vite@5.4.21(@types/node@22.20.1)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0)) acorn-walk: 8.3.5 - autoprefixer: 10.5.4(postcss@8.5.26) + autoprefixer: 10.5.4(postcss@8.5.28) babel-plugin-transform-taroapi: 4.1.3(@babel/core@7.29.7) connect-history-api-fallback: 2.0.0 fast-glob: 3.3.3 @@ -17274,14 +17403,14 @@ snapshots: magic-string: 0.30.21 mrmime: 2.0.1 picomatch: 4.0.4 - postcss: 8.5.26 - postcss-css-variables: 0.19.0(postcss@8.5.26) - postcss-html-transform: 4.1.3(postcss@8.5.26) - postcss-import: 16.2.0(postcss@8.5.26) - postcss-load-config: 5.1.0(jiti@2.7.0)(postcss@8.5.26)(tsx@4.23.13) - postcss-modules: 6.0.1(postcss@8.5.26) - postcss-plugin-constparse: 4.1.3(postcss@8.5.26) - postcss-pxtransform: 4.1.3(postcss@8.5.26) + postcss: 8.5.28 + postcss-css-variables: 0.19.0(postcss@8.5.28) + postcss-html-transform: 4.1.3(postcss@8.5.28) + postcss-import: 16.2.0(postcss@8.5.28) + postcss-load-config: 5.1.0(jiti@2.7.0)(postcss@8.5.28)(tsx@4.23.13) + postcss-modules: 6.0.1(postcss@8.5.28) + postcss-plugin-constparse: 4.1.3(postcss@8.5.28) + postcss-pxtransform: 4.1.3(postcss@8.5.28) regenerator-runtime: 0.11.1 sax: 1.2.4 stylelint: 16.26.1(typescript@5.9.3) @@ -17297,71 +17426,71 @@ snapshots: - tsx - typescript - '@tarojs/webpack5-prebundle@4.1.3(@swc/helpers@0.5.23)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26))': + '@tarojs/webpack5-prebundle@4.1.3(@swc/helpers@0.5.23)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28))': dependencies: '@tarojs/helper': 4.1.3(@swc/helpers@0.5.23) '@tarojs/shared': 4.1.3 enhanced-resolve: 5.22.1 es-module-lexer: 0.10.5 lodash: 4.18.1 - webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) webpack-virtual-modules: 0.6.2 transitivePeerDependencies: - '@swc/helpers' - supports-color - '@tarojs/webpack5-runner@4.1.3(@babel/core@7.29.7)(@swc/core@1.3.96(@swc/helpers@0.5.23))(@swc/helpers@0.5.23)(@tarojs/runtime@4.1.3)(less@3.13.1)(postcss@8.5.26)(sass@1.103.1)(stylus@0.64.0)(typescript@5.9.3)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26))': + '@tarojs/webpack5-runner@4.1.3(@babel/core@7.29.7)(@swc/core@1.3.96(@swc/helpers@0.5.23))(@swc/helpers@0.5.23)(@tarojs/runtime@4.1.3)(less@3.13.1)(postcss@8.5.28)(sass@1.103.1)(stylus@0.64.0)(typescript@5.9.3)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28))': dependencies: '@babel/core': 7.29.7 '@tarojs/helper': 4.1.3(@swc/helpers@0.5.23) '@tarojs/runner-utils': 4.1.3(@swc/helpers@0.5.23) '@tarojs/runtime': 4.1.3 '@tarojs/shared': 4.1.3 - '@tarojs/taro-loader': 4.1.3(@swc/helpers@0.5.23)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) - '@tarojs/webpack5-prebundle': 4.1.3(@swc/helpers@0.5.23)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + '@tarojs/taro-loader': 4.1.3(@swc/helpers@0.5.23)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) + '@tarojs/webpack5-prebundle': 4.1.3(@swc/helpers@0.5.23)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) acorn: 8.16.0 acorn-walk: 8.3.5 - autoprefixer: 10.5.4(postcss@8.5.26) - babel-loader: 8.2.1(@babel/core@7.29.7)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) - copy-webpack-plugin: 12.0.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) - css-loader: 7.1.4(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) - css-minimizer-webpack-plugin: 6.0.0(esbuild@0.21.5)(lightningcss@1.32.0)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + autoprefixer: 10.5.4(postcss@8.5.28) + babel-loader: 8.2.1(@babel/core@7.29.7)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) + copy-webpack-plugin: 12.0.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) + css-loader: 7.1.4(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) + css-minimizer-webpack-plugin: 6.0.0(esbuild@0.21.5)(lightningcss@1.32.0)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) detect-port: 1.6.1 esbuild: 0.21.5 - esbuild-loader: 4.4.3(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + esbuild-loader: 4.4.3(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) html-minifier: 4.0.0 - html-webpack-plugin: 5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + html-webpack-plugin: 5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) jsdom: 24.1.3 - less-loader: 12.3.3(less@3.13.1)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + less-loader: 12.3.3(less@3.13.1)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) lightningcss: 1.32.0 loader-utils: 3.3.1 lodash: 4.18.1 md5: 2.3.0 - mini-css-extract-plugin: 2.10.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + mini-css-extract-plugin: 2.10.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) miniprogram-simulate: 1.6.1 ora: 5.4.1 picomatch: 4.0.4 - postcss: 8.5.26 - postcss-html-transform: 4.1.3(postcss@8.5.26) - postcss-import: 16.2.0(postcss@8.5.26) - postcss-loader: 8.2.1(postcss@8.5.26)(typescript@5.9.3)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) - postcss-plugin-constparse: 4.1.3(postcss@8.5.26) - postcss-pxtransform: 4.1.3(postcss@8.5.26) - postcss-url: 10.1.4(postcss@8.5.26) + postcss: 8.5.28 + postcss-html-transform: 4.1.3(postcss@8.5.28) + postcss-import: 16.2.0(postcss@8.5.28) + postcss-loader: 8.2.1(postcss@8.5.28)(typescript@5.9.3)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) + postcss-plugin-constparse: 4.1.3(postcss@8.5.28) + postcss-pxtransform: 4.1.3(postcss@8.5.28) + postcss-url: 10.1.4(postcss@8.5.28) regenerator-runtime: 0.11.1 resolve-url-loader: 5.0.0 - sass-loader: 14.2.1(sass@1.103.1)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + sass-loader: 14.2.1(sass@1.103.1)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) sax: 1.2.4 - style-loader: 3.3.4(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) - stylus-loader: 8.1.3(stylus@0.64.0)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) - terser-webpack-plugin: 5.6.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(esbuild@0.21.5)(lightningcss@1.32.0)(postcss@8.5.26)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + style-loader: 3.3.4(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) + stylus-loader: 8.1.3(stylus@0.64.0)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) + terser-webpack-plugin: 5.6.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(esbuild@0.21.5)(lightningcss@1.32.0)(postcss@8.5.28)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) vm2: 3.11.5 - webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) webpack-chain: 6.5.1 - webpack-dev-server: 4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + webpack-dev-server: 4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) webpack-format-messages: 3.0.1 webpack-virtual-modules: 0.6.2 - webpackbar: 5.0.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + webpackbar: 5.0.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) optionalDependencies: less: 3.13.1 sass: 1.103.1 @@ -17640,12 +17769,12 @@ snapshots: '@types/postcss-import@14.0.3': dependencies: - postcss: 8.5.26 + postcss: 8.5.28 '@types/postcss-url@10.0.4': dependencies: '@types/node': 20.19.43 - postcss: 8.5.26 + postcss: 8.5.28 '@types/prop-types@15.7.15': {} @@ -18221,6 +18350,14 @@ snapshots: optionalDependencies: vite: 5.4.21(@types/node@20.19.43)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0) + '@vitest/mocker@3.2.7(vite@5.4.21(@types/node@22.20.1)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0))': + dependencies: + '@vitest/spy': 3.2.7 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 5.4.21(@types/node@22.20.1)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0) + '@vitest/pretty-format@2.1.9': dependencies: tinyrainbow: 1.2.0 @@ -18784,13 +18921,13 @@ snapshots: stubborn-fs: 2.0.0 when-exit: 2.1.5 - autoprefixer@10.5.4(postcss@8.5.26): + autoprefixer@10.5.4(postcss@8.5.28): dependencies: browserslist: 4.28.8 caniuse-lite: 1.0.30001810 fraction.js: 5.3.4 picocolors: 1.1.1 - postcss: 8.5.26 + postcss: 8.5.28 postcss-value-parser: 4.2.0 available-typed-arrays@1.0.7: @@ -18837,7 +18974,7 @@ snapshots: transitivePeerDependencies: - supports-color - babel-loader@8.2.1(@babel/core@7.29.7)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)): + babel-loader@8.2.1(@babel/core@7.29.7)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)): dependencies: '@babel/core': 7.29.7 find-cache-dir: 2.1.0 @@ -18845,7 +18982,7 @@ snapshots: make-dir: 2.1.0 pify: 4.0.1 schema-utils: 2.7.1 - webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) babel-plugin-const-enum@1.2.0(@babel/core@7.29.7): dependencies: @@ -19790,7 +19927,7 @@ snapshots: copy-text-to-clipboard@3.2.2: {} - copy-webpack-plugin@12.0.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)): + copy-webpack-plugin@12.0.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)): dependencies: fast-glob: 3.3.3 glob-parent: 6.0.2 @@ -19798,7 +19935,7 @@ snapshots: normalize-path: 3.0.0 schema-utils: 4.3.3 serialize-javascript: 6.0.2 - webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) core-js-compat@3.49.0: dependencies: @@ -19871,34 +20008,34 @@ snapshots: crypto-random-string@1.0.0: {} - css-declaration-sorter@7.4.0(postcss@8.5.26): + css-declaration-sorter@7.4.0(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 css-functions-list@3.3.3: {} - css-loader@7.1.4(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)): + css-loader@7.1.4(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)): dependencies: - icss-utils: 5.1.0(postcss@8.5.26) - postcss: 8.5.26 - postcss-modules-extract-imports: 3.1.0(postcss@8.5.26) - postcss-modules-local-by-default: 4.2.0(postcss@8.5.26) - postcss-modules-scope: 3.2.1(postcss@8.5.26) - postcss-modules-values: 4.0.0(postcss@8.5.26) + icss-utils: 5.1.0(postcss@8.5.28) + postcss: 8.5.28 + postcss-modules-extract-imports: 3.1.0(postcss@8.5.28) + postcss-modules-local-by-default: 4.2.0(postcss@8.5.28) + postcss-modules-scope: 3.2.1(postcss@8.5.28) + postcss-modules-values: 4.0.0(postcss@8.5.28) postcss-value-parser: 4.2.0 semver: 7.8.1 optionalDependencies: - webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) - css-minimizer-webpack-plugin@6.0.0(esbuild@0.21.5)(lightningcss@1.32.0)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)): + css-minimizer-webpack-plugin@6.0.0(esbuild@0.21.5)(lightningcss@1.32.0)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)): dependencies: '@jridgewell/trace-mapping': 0.3.31 - cssnano: 6.1.2(postcss@8.5.26) + cssnano: 6.1.2(postcss@8.5.28) jest-worker: 29.7.0 - postcss: 8.5.26 + postcss: 8.5.28 schema-utils: 4.3.3 serialize-javascript: 6.0.2 - webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) optionalDependencies: esbuild: 0.21.5 lightningcss: 1.32.0 @@ -19947,49 +20084,49 @@ snapshots: cssfontparser@1.2.1: {} - cssnano-preset-default@6.1.2(postcss@8.5.26): + cssnano-preset-default@6.1.2(postcss@8.5.28): dependencies: browserslist: 4.28.2 - css-declaration-sorter: 7.4.0(postcss@8.5.26) - cssnano-utils: 4.0.2(postcss@8.5.26) - postcss: 8.5.26 - postcss-calc: 9.0.1(postcss@8.5.26) - postcss-colormin: 6.1.0(postcss@8.5.26) - postcss-convert-values: 6.1.0(postcss@8.5.26) - postcss-discard-comments: 6.0.2(postcss@8.5.26) - postcss-discard-duplicates: 6.0.3(postcss@8.5.26) - postcss-discard-empty: 6.0.3(postcss@8.5.26) - postcss-discard-overridden: 6.0.2(postcss@8.5.26) - postcss-merge-longhand: 6.0.5(postcss@8.5.26) - postcss-merge-rules: 6.1.1(postcss@8.5.26) - postcss-minify-font-values: 6.1.0(postcss@8.5.26) - postcss-minify-gradients: 6.0.3(postcss@8.5.26) - postcss-minify-params: 6.1.0(postcss@8.5.26) - postcss-minify-selectors: 6.0.4(postcss@8.5.26) - postcss-normalize-charset: 6.0.2(postcss@8.5.26) - postcss-normalize-display-values: 6.0.2(postcss@8.5.26) - postcss-normalize-positions: 6.0.2(postcss@8.5.26) - postcss-normalize-repeat-style: 6.0.2(postcss@8.5.26) - postcss-normalize-string: 6.0.2(postcss@8.5.26) - postcss-normalize-timing-functions: 6.0.2(postcss@8.5.26) - postcss-normalize-unicode: 6.1.0(postcss@8.5.26) - postcss-normalize-url: 6.0.2(postcss@8.5.26) - postcss-normalize-whitespace: 6.0.2(postcss@8.5.26) - postcss-ordered-values: 6.0.2(postcss@8.5.26) - postcss-reduce-initial: 6.1.0(postcss@8.5.26) - postcss-reduce-transforms: 6.0.2(postcss@8.5.26) - postcss-svgo: 6.0.3(postcss@8.5.26) - postcss-unique-selectors: 6.0.4(postcss@8.5.26) - - cssnano-utils@4.0.2(postcss@8.5.26): - dependencies: - postcss: 8.5.26 - - cssnano@6.1.2(postcss@8.5.26): - dependencies: - cssnano-preset-default: 6.1.2(postcss@8.5.26) + css-declaration-sorter: 7.4.0(postcss@8.5.28) + cssnano-utils: 4.0.2(postcss@8.5.28) + postcss: 8.5.28 + postcss-calc: 9.0.1(postcss@8.5.28) + postcss-colormin: 6.1.0(postcss@8.5.28) + postcss-convert-values: 6.1.0(postcss@8.5.28) + postcss-discard-comments: 6.0.2(postcss@8.5.28) + postcss-discard-duplicates: 6.0.3(postcss@8.5.28) + postcss-discard-empty: 6.0.3(postcss@8.5.28) + postcss-discard-overridden: 6.0.2(postcss@8.5.28) + postcss-merge-longhand: 6.0.5(postcss@8.5.28) + postcss-merge-rules: 6.1.1(postcss@8.5.28) + postcss-minify-font-values: 6.1.0(postcss@8.5.28) + postcss-minify-gradients: 6.0.3(postcss@8.5.28) + postcss-minify-params: 6.1.0(postcss@8.5.28) + postcss-minify-selectors: 6.0.4(postcss@8.5.28) + postcss-normalize-charset: 6.0.2(postcss@8.5.28) + postcss-normalize-display-values: 6.0.2(postcss@8.5.28) + postcss-normalize-positions: 6.0.2(postcss@8.5.28) + postcss-normalize-repeat-style: 6.0.2(postcss@8.5.28) + postcss-normalize-string: 6.0.2(postcss@8.5.28) + postcss-normalize-timing-functions: 6.0.2(postcss@8.5.28) + postcss-normalize-unicode: 6.1.0(postcss@8.5.28) + postcss-normalize-url: 6.0.2(postcss@8.5.28) + postcss-normalize-whitespace: 6.0.2(postcss@8.5.28) + postcss-ordered-values: 6.0.2(postcss@8.5.28) + postcss-reduce-initial: 6.1.0(postcss@8.5.28) + postcss-reduce-transforms: 6.0.2(postcss@8.5.28) + postcss-svgo: 6.0.3(postcss@8.5.28) + postcss-unique-selectors: 6.0.4(postcss@8.5.28) + + cssnano-utils@4.0.2(postcss@8.5.28): + dependencies: + postcss: 8.5.28 + + cssnano@6.1.2(postcss@8.5.28): + dependencies: + cssnano-preset-default: 6.1.2(postcss@8.5.28) lilconfig: 3.1.3 - postcss: 8.5.26 + postcss: 8.5.28 csso@3.5.1: dependencies: @@ -20615,12 +20752,12 @@ snapshots: esast-util-from-estree: 2.0.0 vfile-message: 4.0.3 - esbuild-loader@4.4.3(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)): + esbuild-loader@4.4.3(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)): dependencies: esbuild: 0.27.7 get-tsconfig: 4.14.0 loader-utils: 2.0.4 - webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) webpack-sources: 3.5.0 esbuild@0.21.5: @@ -22331,12 +22468,12 @@ snapshots: readable-stream: 1.1.14 streamqueue: 0.0.6 - gulp-postcss@10.0.0(jiti@2.7.0)(postcss@8.5.26)(tsx@4.23.13): + gulp-postcss@10.0.0(jiti@2.7.0)(postcss@8.5.28)(tsx@4.23.13): dependencies: fancy-log: 2.0.0 plugin-error: 2.0.1 - postcss: 8.5.26 - postcss-load-config: 5.1.0(jiti@2.7.0)(postcss@8.5.26)(tsx@4.23.13) + postcss: 8.5.28 + postcss-load-config: 5.1.0(jiti@2.7.0)(postcss@8.5.28)(tsx@4.23.13) vinyl-sourcemaps-apply: 0.2.1 transitivePeerDependencies: - jiti @@ -22590,7 +22727,7 @@ snapshots: html-void-elements@3.0.0: {} - html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)): + html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)): dependencies: '@types/html-minifier-terser': 6.1.0 html-minifier-terser: 6.1.0 @@ -22598,10 +22735,10 @@ snapshots: pretty-error: 4.0.0 tapable: 2.3.3 optionalDependencies: - webpack: 5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) optional: true - html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)): + html-webpack-plugin@5.6.7(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)): dependencies: '@types/html-minifier-terser': 6.1.0 html-minifier-terser: 6.1.0 @@ -22609,7 +22746,7 @@ snapshots: pretty-error: 4.0.0 tapable: 2.3.3 optionalDependencies: - webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) htmlparser2@6.1.0: dependencies: @@ -22737,9 +22874,9 @@ snapshots: runes2: 1.1.4 yup: 1.7.1 - icss-utils@5.1.0(postcss@8.5.26): + icss-utils@5.1.0(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 ieee754@1.2.1: {} @@ -22795,12 +22932,12 @@ snapshots: inline-style-parser@0.2.7: {} - inquirer@12.3.0(@types/node@20.19.43): + inquirer@12.3.0(@types/node@22.20.1): dependencies: - '@inquirer/core': 10.3.2(@types/node@20.19.43) - '@inquirer/prompts': 7.10.1(@types/node@20.19.43) - '@inquirer/type': 3.0.10(@types/node@20.19.43) - '@types/node': 20.19.43 + '@inquirer/core': 10.3.2(@types/node@22.20.1) + '@inquirer/prompts': 7.10.1(@types/node@22.20.1) + '@inquirer/type': 3.0.10(@types/node@22.20.1) + '@types/node': 22.20.1 ansi-escapes: 4.3.2 mute-stream: 2.0.0 run-async: 3.0.0 @@ -23310,7 +23447,7 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@30.4.2(@types/node@20.19.43): + jest-cli@30.4.2(@types/node@22.20.1): dependencies: '@jest/core': 30.4.2 '@jest/test-result': 30.4.1 @@ -23318,7 +23455,7 @@ snapshots: chalk: 4.1.2 exit-x: 0.2.2 import-local: 3.2.0 - jest-config: 30.4.2(@types/node@20.19.43) + jest-config: 30.4.2(@types/node@22.20.1) jest-util: 30.4.1 jest-validate: 30.4.1 yargs: 17.7.3 @@ -23360,7 +23497,7 @@ snapshots: - babel-plugin-macros - supports-color - jest-config@30.4.2(@types/node@20.19.43): + jest-config@30.4.2(@types/node@22.20.1): dependencies: '@babel/core': 7.29.7 '@jest/get-type': 30.1.0 @@ -23386,7 +23523,7 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 20.19.43 + '@types/node': 22.20.1 transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -23629,12 +23766,12 @@ snapshots: merge-stream: 2.0.0 supports-color: 8.1.1 - jest@30.4.2(@types/node@20.19.43): + jest@30.4.2(@types/node@22.20.1): dependencies: '@jest/core': 30.4.2 '@jest/types': 30.4.1 import-local: 3.2.0 - jest-cli: 30.4.2(@types/node@20.19.43) + jest-cli: 30.4.2(@types/node@22.20.1) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -23878,11 +24015,11 @@ snapshots: lead@4.0.0: {} - less-loader@12.3.3(less@3.13.1)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)): + less-loader@12.3.3(less@3.13.1)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)): dependencies: less: 3.13.1 optionalDependencies: - webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) less@3.13.1: dependencies: @@ -24824,11 +24961,11 @@ snapshots: min-indent@1.0.1: {} - mini-css-extract-plugin@2.10.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)): + mini-css-extract-plugin@2.10.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)): dependencies: schema-utils: 4.3.3 tapable: 2.3.3 - webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) minimalistic-assert@1.0.1: {} @@ -25678,244 +25815,244 @@ snapshots: possible-typed-array-names@1.1.0: {} - postcss-calc@9.0.1(postcss@8.5.26): + postcss-calc@9.0.1(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 postcss-selector-parser: 6.1.2 postcss-value-parser: 4.2.0 - postcss-colormin@6.1.0(postcss@8.5.26): + postcss-colormin@6.1.0(postcss@8.5.28): dependencies: browserslist: 4.28.2 caniuse-api: 3.0.0 colord: 2.9.3 - postcss: 8.5.26 + postcss: 8.5.28 postcss-value-parser: 4.2.0 - postcss-convert-values@6.1.0(postcss@8.5.26): + postcss-convert-values@6.1.0(postcss@8.5.28): dependencies: browserslist: 4.28.2 - postcss: 8.5.26 + postcss: 8.5.28 postcss-value-parser: 4.2.0 - postcss-css-variables@0.19.0(postcss@8.5.26): + postcss-css-variables@0.19.0(postcss@8.5.28): dependencies: balanced-match: 1.0.2 escape-string-regexp: 1.0.5 extend: 3.0.2 - postcss: 8.5.26 + postcss: 8.5.28 - postcss-discard-comments@6.0.2(postcss@8.5.26): + postcss-discard-comments@6.0.2(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 - postcss-discard-duplicates@6.0.3(postcss@8.5.26): + postcss-discard-duplicates@6.0.3(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 - postcss-discard-empty@6.0.3(postcss@8.5.26): + postcss-discard-empty@6.0.3(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 - postcss-discard-overridden@6.0.2(postcss@8.5.26): + postcss-discard-overridden@6.0.2(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 - postcss-html-transform@4.1.3(postcss@8.5.26): + postcss-html-transform@4.1.3(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 - postcss-import@16.2.0(postcss@8.5.26): + postcss-import@16.2.0(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.12 - postcss-load-config@5.1.0(jiti@2.7.0)(postcss@8.5.26)(tsx@4.23.13): + postcss-load-config@5.1.0(jiti@2.7.0)(postcss@8.5.28)(tsx@4.23.13): dependencies: lilconfig: 3.1.3 yaml: 2.9.0 optionalDependencies: jiti: 2.7.0 - postcss: 8.5.26 + postcss: 8.5.28 tsx: 4.23.13 - postcss-load-config@6.0.1(jiti@2.7.0)(postcss@8.5.26)(tsx@4.23.13)(yaml@2.9.0): + postcss-load-config@6.0.1(jiti@2.7.0)(postcss@8.5.28)(tsx@4.23.13)(yaml@2.9.0): dependencies: lilconfig: 3.1.3 optionalDependencies: jiti: 2.7.0 - postcss: 8.5.26 + postcss: 8.5.28 tsx: 4.23.13 yaml: 2.9.0 - postcss-loader@8.2.1(postcss@8.5.26)(typescript@5.9.3)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)): + postcss-loader@8.2.1(postcss@8.5.28)(typescript@5.9.3)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)): dependencies: cosmiconfig: 9.0.1(typescript@5.9.3) jiti: 2.7.0 - postcss: 8.5.26 + postcss: 8.5.28 semver: 7.8.1 optionalDependencies: - webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) transitivePeerDependencies: - typescript - postcss-merge-longhand@6.0.5(postcss@8.5.26): + postcss-merge-longhand@6.0.5(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 postcss-value-parser: 4.2.0 - stylehacks: 6.1.1(postcss@8.5.26) + stylehacks: 6.1.1(postcss@8.5.28) - postcss-merge-rules@6.1.1(postcss@8.5.26): + postcss-merge-rules@6.1.1(postcss@8.5.28): dependencies: browserslist: 4.28.2 caniuse-api: 3.0.0 - cssnano-utils: 4.0.2(postcss@8.5.26) - postcss: 8.5.26 + cssnano-utils: 4.0.2(postcss@8.5.28) + postcss: 8.5.28 postcss-selector-parser: 6.1.2 - postcss-minify-font-values@6.1.0(postcss@8.5.26): + postcss-minify-font-values@6.1.0(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 postcss-value-parser: 4.2.0 - postcss-minify-gradients@6.0.3(postcss@8.5.26): + postcss-minify-gradients@6.0.3(postcss@8.5.28): dependencies: colord: 2.9.3 - cssnano-utils: 4.0.2(postcss@8.5.26) - postcss: 8.5.26 + cssnano-utils: 4.0.2(postcss@8.5.28) + postcss: 8.5.28 postcss-value-parser: 4.2.0 - postcss-minify-params@6.1.0(postcss@8.5.26): + postcss-minify-params@6.1.0(postcss@8.5.28): dependencies: browserslist: 4.28.2 - cssnano-utils: 4.0.2(postcss@8.5.26) - postcss: 8.5.26 + cssnano-utils: 4.0.2(postcss@8.5.28) + postcss: 8.5.28 postcss-value-parser: 4.2.0 - postcss-minify-selectors@6.0.4(postcss@8.5.26): + postcss-minify-selectors@6.0.4(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 postcss-selector-parser: 6.1.2 - postcss-modules-extract-imports@3.1.0(postcss@8.5.26): + postcss-modules-extract-imports@3.1.0(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 - postcss-modules-local-by-default@4.2.0(postcss@8.5.26): + postcss-modules-local-by-default@4.2.0(postcss@8.5.28): dependencies: - icss-utils: 5.1.0(postcss@8.5.26) - postcss: 8.5.26 + icss-utils: 5.1.0(postcss@8.5.28) + postcss: 8.5.28 postcss-selector-parser: 7.1.1 postcss-value-parser: 4.2.0 - postcss-modules-scope@3.2.1(postcss@8.5.26): + postcss-modules-scope@3.2.1(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 postcss-selector-parser: 7.1.1 - postcss-modules-values@4.0.0(postcss@8.5.26): + postcss-modules-values@4.0.0(postcss@8.5.28): dependencies: - icss-utils: 5.1.0(postcss@8.5.26) - postcss: 8.5.26 + icss-utils: 5.1.0(postcss@8.5.28) + postcss: 8.5.28 - postcss-modules@6.0.1(postcss@8.5.26): + postcss-modules@6.0.1(postcss@8.5.28): dependencies: generic-names: 4.0.0 - icss-utils: 5.1.0(postcss@8.5.26) + icss-utils: 5.1.0(postcss@8.5.28) lodash.camelcase: 4.3.0 - postcss: 8.5.26 - postcss-modules-extract-imports: 3.1.0(postcss@8.5.26) - postcss-modules-local-by-default: 4.2.0(postcss@8.5.26) - postcss-modules-scope: 3.2.1(postcss@8.5.26) - postcss-modules-values: 4.0.0(postcss@8.5.26) + postcss: 8.5.28 + postcss-modules-extract-imports: 3.1.0(postcss@8.5.28) + postcss-modules-local-by-default: 4.2.0(postcss@8.5.28) + postcss-modules-scope: 3.2.1(postcss@8.5.28) + postcss-modules-values: 4.0.0(postcss@8.5.28) string-hash: 1.1.3 - postcss-normalize-charset@6.0.2(postcss@8.5.26): + postcss-normalize-charset@6.0.2(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 - postcss-normalize-display-values@6.0.2(postcss@8.5.26): + postcss-normalize-display-values@6.0.2(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 postcss-value-parser: 4.2.0 - postcss-normalize-positions@6.0.2(postcss@8.5.26): + postcss-normalize-positions@6.0.2(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 postcss-value-parser: 4.2.0 - postcss-normalize-repeat-style@6.0.2(postcss@8.5.26): + postcss-normalize-repeat-style@6.0.2(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 postcss-value-parser: 4.2.0 - postcss-normalize-string@6.0.2(postcss@8.5.26): + postcss-normalize-string@6.0.2(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 postcss-value-parser: 4.2.0 - postcss-normalize-timing-functions@6.0.2(postcss@8.5.26): + postcss-normalize-timing-functions@6.0.2(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 postcss-value-parser: 4.2.0 - postcss-normalize-unicode@6.1.0(postcss@8.5.26): + postcss-normalize-unicode@6.1.0(postcss@8.5.28): dependencies: browserslist: 4.28.2 - postcss: 8.5.26 + postcss: 8.5.28 postcss-value-parser: 4.2.0 - postcss-normalize-url@6.0.2(postcss@8.5.26): + postcss-normalize-url@6.0.2(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 postcss-value-parser: 4.2.0 - postcss-normalize-whitespace@6.0.2(postcss@8.5.26): + postcss-normalize-whitespace@6.0.2(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 postcss-value-parser: 4.2.0 - postcss-ordered-values@6.0.2(postcss@8.5.26): + postcss-ordered-values@6.0.2(postcss@8.5.28): dependencies: - cssnano-utils: 4.0.2(postcss@8.5.26) - postcss: 8.5.26 + cssnano-utils: 4.0.2(postcss@8.5.28) + postcss: 8.5.28 postcss-value-parser: 4.2.0 - postcss-plugin-constparse@4.1.3(postcss@8.5.26): + postcss-plugin-constparse@4.1.3(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 - postcss-pxtransform@4.1.3(postcss@8.5.26): + postcss-pxtransform@4.1.3(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 - postcss-reduce-initial@6.1.0(postcss@8.5.26): + postcss-reduce-initial@6.1.0(postcss@8.5.28): dependencies: browserslist: 4.28.2 caniuse-api: 3.0.0 - postcss: 8.5.26 + postcss: 8.5.28 - postcss-reduce-transforms@6.0.2(postcss@8.5.26): + postcss-reduce-transforms@6.0.2(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 postcss-value-parser: 4.2.0 postcss-resolve-nested-selector@0.1.6: {} - postcss-rtlcss@5.7.1(postcss@8.5.26): + postcss-rtlcss@5.7.1(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 rtlcss: 4.3.0 - postcss-safe-parser@7.0.1(postcss@8.5.26): + postcss-safe-parser@7.0.1(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 - postcss-scss@4.0.9(postcss@8.5.26): + postcss-scss@4.0.9(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 postcss-selector-parser@6.1.2: dependencies: @@ -25927,23 +26064,23 @@ snapshots: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss-svgo@6.0.3(postcss@8.5.26): + postcss-svgo@6.0.3(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 postcss-value-parser: 4.2.0 svgo: 3.3.3 - postcss-unique-selectors@6.0.4(postcss@8.5.26): + postcss-unique-selectors@6.0.4(postcss@8.5.28): dependencies: - postcss: 8.5.26 + postcss: 8.5.28 postcss-selector-parser: 6.1.2 - postcss-url@10.1.4(postcss@8.5.26): + postcss-url@10.1.4(postcss@8.5.28): dependencies: make-dir: 3.1.0 mime: 2.5.2 minimatch: 3.1.5 - postcss: 8.5.26 + postcss: 8.5.28 xxhashjs: 0.2.2 postcss-value-parser@4.2.0: {} @@ -25953,7 +26090,7 @@ snapshots: picocolors: 0.2.1 source-map: 0.6.1 - postcss@8.5.26: + postcss@8.5.28: dependencies: nanoid: 3.3.18 picocolors: 1.1.1 @@ -26534,7 +26671,7 @@ snapshots: relateurl@0.2.7: {} - release-it@18.1.2(@types/node@20.19.43)(typescript@5.9.3): + release-it@18.1.2(@types/node@22.20.1)(typescript@5.9.3): dependencies: '@iarna/toml': 2.2.5 '@octokit/rest': 21.0.2 @@ -26545,7 +26682,7 @@ snapshots: execa: 9.5.2 git-url-parse: 16.0.0 globby: 14.0.2 - inquirer: 12.3.0(@types/node@20.19.43) + inquirer: 12.3.0(@types/node@22.20.1) issue-parser: 7.0.1 lodash: 4.17.21 mime-types: 2.1.35 @@ -26685,7 +26822,7 @@ snapshots: adjust-sourcemap-loader: 4.0.0 convert-source-map: 1.9.0 loader-utils: 2.0.4 - postcss: 8.5.26 + postcss: 8.5.28 source-map: 0.6.1 resolve@1.22.12: @@ -26820,7 +26957,7 @@ snapshots: dependencies: escalade: 3.2.0 picocolors: 1.1.1 - postcss: 8.5.26 + postcss: 8.5.28 strip-json-comments: 3.1.1 run-applescript@7.1.0: {} @@ -26872,12 +27009,12 @@ snapshots: safer-buffer@2.1.2: {} - sass-loader@14.2.1(sass@1.103.1)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)): + sass-loader@14.2.1(sass@1.103.1)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)): dependencies: neo-async: 2.6.2 optionalDependencies: sass: 1.103.1 - webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) sass@1.103.1: dependencies: @@ -27586,9 +27723,9 @@ snapshots: stubborn-utils@1.0.2: {} - style-loader@3.3.4(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)): + style-loader@3.3.4(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)): dependencies: - webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) style-to-js@1.1.21: dependencies: @@ -27598,10 +27735,10 @@ snapshots: dependencies: inline-style-parser: 0.2.7 - stylehacks@6.1.1(postcss@8.5.26): + stylehacks@6.1.1(postcss@8.5.28): dependencies: browserslist: 4.28.2 - postcss: 8.5.26 + postcss: 8.5.28 postcss-selector-parser: 6.1.2 stylelint@16.26.1(typescript@5.9.3): @@ -27634,9 +27771,9 @@ snapshots: micromatch: 4.0.8 normalize-path: 3.0.0 picocolors: 1.1.1 - postcss: 8.5.26 + postcss: 8.5.28 postcss-resolve-nested-selector: 0.1.6 - postcss-safe-parser: 7.0.1(postcss@8.5.26) + postcss-safe-parser: 7.0.1(postcss@8.5.28) postcss-selector-parser: 7.1.1 postcss-value-parser: 4.2.0 resolve-from: 5.0.0 @@ -27649,13 +27786,13 @@ snapshots: - supports-color - typescript - stylus-loader@8.1.3(stylus@0.64.0)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)): + stylus-loader@8.1.3(stylus@0.64.0)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)): dependencies: fast-glob: 3.3.3 normalize-path: 3.0.0 stylus: 0.64.0 optionalDependencies: - webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) stylus@0.64.0: dependencies: @@ -27774,30 +27911,30 @@ snapshots: dependencies: execa: 0.7.0 - terser-webpack-plugin@5.6.1(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)): + terser-webpack-plugin@5.6.1(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 terser: 5.48.0 - webpack: 5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) optionalDependencies: '@swc/core': 1.16.1(@swc/helpers@0.5.23) lightningcss: 1.32.0 - postcss: 8.5.26 + postcss: 8.5.28 - terser-webpack-plugin@5.6.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(esbuild@0.21.5)(lightningcss@1.32.0)(postcss@8.5.26)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)): + terser-webpack-plugin@5.6.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(esbuild@0.21.5)(lightningcss@1.32.0)(postcss@8.5.28)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 terser: 5.48.0 - webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) optionalDependencies: '@swc/core': 1.3.96(@swc/helpers@0.5.23) esbuild: 0.21.5 lightningcss: 1.32.0 - postcss: 8.5.26 + postcss: 8.5.28 terser@5.48.0: dependencies: @@ -27968,12 +28105,12 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-jest@29.4.12(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(jest-util@30.4.1)(jest@30.4.2(@types/node@20.19.43))(typescript@5.9.3): + ts-jest@29.4.12(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(jest-util@30.4.1)(jest@30.4.2(@types/node@22.20.1))(typescript@5.9.3): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 handlebars: 4.7.9 - jest: 30.4.2(@types/node@20.19.43) + jest: 30.4.2(@types/node@22.20.1) json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 @@ -27999,7 +28136,7 @@ snapshots: tslib@2.8.1: {} - tsup@8.5.1(@microsoft/api-extractor@7.59.0(@types/node@22.20.1))(@swc/core@1.16.1(@swc/helpers@0.5.23))(jiti@2.7.0)(postcss@8.5.26)(tsx@4.23.13)(typescript@5.9.3)(yaml@2.9.0): + tsup@8.5.1(@microsoft/api-extractor@7.59.0(@types/node@22.20.1))(@swc/core@1.16.1(@swc/helpers@0.5.23))(jiti@2.7.0)(postcss@8.5.28)(tsx@4.23.13)(typescript@5.9.3)(yaml@2.9.0): dependencies: bundle-require: 5.1.0(esbuild@0.27.7) cac: 6.7.14 @@ -28010,7 +28147,7 @@ snapshots: fix-dts-default-cjs-exports: 1.0.1 joycon: 3.1.1 picocolors: 1.1.1 - postcss-load-config: 6.0.1(jiti@2.7.0)(postcss@8.5.26)(tsx@4.23.13)(yaml@2.9.0) + postcss-load-config: 6.0.1(jiti@2.7.0)(postcss@8.5.28)(tsx@4.23.13)(yaml@2.9.0) resolve-from: 5.0.0 rollup: 4.63.1 source-map: 0.7.6 @@ -28021,7 +28158,7 @@ snapshots: optionalDependencies: '@microsoft/api-extractor': 7.59.0(@types/node@22.20.1) '@swc/core': 1.16.1(@swc/helpers@0.5.23) - postcss: 8.5.26 + postcss: 8.5.28 typescript: 5.9.3 transitivePeerDependencies: - jiti @@ -28558,6 +28695,24 @@ snapshots: - supports-color - terser + vite-node@3.2.4(@types/node@22.20.1)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0): + dependencies: + cac: 6.7.14 + debug: 4.4.3(supports-color@8.1.1) + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 5.4.21(@types/node@22.20.1)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + vite-plugin-dts@4.5.4(@types/node@20.19.43)(rollup@4.63.1)(typescript@5.9.3)(vite@5.4.21(@types/node@20.19.43)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0)): dependencies: '@microsoft/api-extractor': 7.59.0(@types/node@20.19.43) @@ -28588,7 +28743,7 @@ snapshots: vite@5.4.21(@types/node@18.19.130)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0): dependencies: esbuild: 0.21.5 - postcss: 8.5.26 + postcss: 8.5.28 rollup: 4.63.1 optionalDependencies: '@types/node': 18.19.130 @@ -28602,7 +28757,7 @@ snapshots: vite@5.4.21(@types/node@20.19.43)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0): dependencies: esbuild: 0.21.5 - postcss: 8.5.26 + postcss: 8.5.28 rollup: 4.63.1 optionalDependencies: '@types/node': 20.19.43 @@ -28616,7 +28771,7 @@ snapshots: vite@5.4.21(@types/node@22.20.1)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0): dependencies: esbuild: 0.21.5 - postcss: 8.5.26 + postcss: 8.5.28 rollup: 4.63.1 optionalDependencies: '@types/node': 22.20.1 @@ -28632,7 +28787,7 @@ snapshots: jest-canvas-mock: 2.5.2 vitest: 3.2.7(@types/debug@4.1.13)(@types/node@20.19.43)(@vitest/ui@2.1.9)(happy-dom@14.12.3)(jsdom@24.1.3)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0) - vitest@1.6.1(@types/node@18.19.130)(@vitest/ui@2.1.9(vitest@3.2.7))(happy-dom@14.12.3)(jsdom@24.1.3)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0): + vitest@1.6.1(@types/node@18.19.130)(@vitest/ui@2.1.9)(happy-dom@14.12.3)(jsdom@24.1.3)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0): dependencies: '@vitest/expect': 1.6.1 '@vitest/runner': 1.6.1 @@ -28669,7 +28824,7 @@ snapshots: - supports-color - terser - vitest@1.6.1(@types/node@22.20.1)(@vitest/ui@2.1.9(vitest@3.2.7))(happy-dom@14.12.3)(jsdom@24.1.3)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0): + vitest@1.6.1(@types/node@22.20.1)(@vitest/ui@2.1.9)(happy-dom@14.12.3)(jsdom@24.1.3)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0): dependencies: '@vitest/expect': 1.6.1 '@vitest/runner': 1.6.1 @@ -28786,6 +28941,48 @@ snapshots: - supports-color - terser + vitest@3.2.7(@types/debug@4.1.13)(@types/node@22.20.1)(@vitest/ui@2.1.9)(happy-dom@14.12.3)(jsdom@24.1.3)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0): + dependencies: + '@types/chai': 5.2.3 + '@vitest/expect': 3.2.7 + '@vitest/mocker': 3.2.7(vite@5.4.21(@types/node@22.20.1)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0)) + '@vitest/pretty-format': 3.2.7 + '@vitest/runner': 3.2.7 + '@vitest/snapshot': 3.2.7 + '@vitest/spy': 3.2.7 + '@vitest/utils': 3.2.7 + chai: 5.3.3 + debug: 4.4.3(supports-color@8.1.1) + expect-type: 1.3.0 + magic-string: 0.30.21 + pathe: 2.0.3 + picomatch: 4.0.4 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.17 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 5.4.21(@types/node@22.20.1)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0) + vite-node: 3.2.4(@types/node@22.20.1)(less@3.13.1)(lightningcss@1.32.0)(sass@1.103.1)(stylus@0.64.0)(terser@5.48.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/debug': 4.1.13 + '@types/node': 22.20.1 + '@vitest/ui': 2.1.9(vitest@3.2.7) + happy-dom: 14.12.3 + jsdom: 24.1.3 + transitivePeerDependencies: + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + vm2@3.11.5: dependencies: acorn: 8.16.0 @@ -28845,26 +29042,26 @@ snapshots: deepmerge: 1.5.2 javascript-stringify: 2.1.0 - webpack-dev-middleware@5.3.4(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)): + webpack-dev-middleware@5.3.4(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)): dependencies: colorette: 2.0.20 memfs: 3.5.3 mime-types: 2.1.35 range-parser: 1.2.1 schema-utils: 4.3.3 - webpack: 5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) optional: true - webpack-dev-middleware@5.3.4(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)): + webpack-dev-middleware@5.3.4(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)): dependencies: colorette: 2.0.20 memfs: 3.5.3 mime-types: 2.1.35 range-parser: 1.2.1 schema-utils: 4.3.3 - webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) - webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)): + webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)): dependencies: '@types/bonjour': 3.5.13 '@types/connect-history-api-fallback': 1.5.4 @@ -28894,10 +29091,10 @@ snapshots: serve-index: 1.9.2 sockjs: 0.3.24 spdy: 4.0.2 - webpack-dev-middleware: 5.3.4(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + webpack-dev-middleware: 5.3.4(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) ws: 8.21.0 optionalDependencies: - webpack: 5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) transitivePeerDependencies: - bufferutil - debug @@ -28905,7 +29102,7 @@ snapshots: - utf-8-validate optional: true - webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)): + webpack-dev-server@4.15.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)): dependencies: '@types/bonjour': 3.5.13 '@types/connect-history-api-fallback': 1.5.4 @@ -28935,10 +29132,10 @@ snapshots: serve-index: 1.9.2 sockjs: 0.3.24 spdy: 4.0.2 - webpack-dev-middleware: 5.3.4(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + webpack-dev-middleware: 5.3.4(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) ws: 8.21.0 optionalDependencies: - webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) transitivePeerDependencies: - bufferutil - debug @@ -28959,7 +29156,7 @@ snapshots: webpack-virtual-modules@0.6.2: {} - webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26): + webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.9 @@ -28982,7 +29179,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.3.3 - terser-webpack-plugin: 5.6.1(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + terser-webpack-plugin: 5.6.1(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)(webpack@5.91.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) watchpack: 2.5.1 webpack-sources: 3.5.0 transitivePeerDependencies: @@ -28999,7 +29196,7 @@ snapshots: - postcss - uglify-js - webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26): + webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.9 @@ -29022,7 +29219,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.3.3 - terser-webpack-plugin: 5.6.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(esbuild@0.21.5)(lightningcss@1.32.0)(postcss@8.5.26)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)) + terser-webpack-plugin: 5.6.1(@swc/core@1.3.96(@swc/helpers@0.5.23))(esbuild@0.21.5)(lightningcss@1.32.0)(postcss@8.5.28)(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)) watchpack: 2.5.1 webpack-sources: 3.5.0 transitivePeerDependencies: @@ -29039,13 +29236,13 @@ snapshots: - postcss - uglify-js - webpackbar@5.0.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26)): + webpackbar@5.0.2(webpack@5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28)): dependencies: chalk: 4.1.2 consola: 2.15.3 pretty-time: 1.1.0 std-env: 3.10.0 - webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.26) + webpack: 5.91.0(@swc/core@1.3.96(@swc/helpers@0.5.23))(lightningcss@1.32.0)(postcss@8.5.28) websocket-driver@0.7.4: dependencies: diff --git a/scripts/properties.json b/scripts/properties.json index aaf7d01bc8..8b3a512a01 100644 --- a/scripts/properties.json +++ b/scripts/properties.json @@ -6949,6 +6949,36 @@ "第三列": "`string`", "第四列": "-" }, + { + "组件分类": "操作反馈", + "组件名": "NoticeBar", + "版本号": "2.0.0", + "表格名": "Props", + "第一列": "description", + "第二列": "副文本内容,显示在主文本下方", + "第三列": "`ReactNode`", + "第四列": "-" + }, + { + "组件分类": "操作反馈", + "组件名": "NoticeBar", + "版本号": "2.0.0", + "表格名": "Props", + "第一列": "tag", + "第二列": "信息标图标,显示在文案右侧,尺寸 12×12", + "第三列": "`ReactNode`", + "第四列": "-" + }, + { + "组件分类": "操作反馈", + "组件名": "NoticeBar", + "版本号": "2.0.0", + "表格名": "Props", + "第一列": "action", + "第二列": "操作按钮区域,支持弱行动(文字链接)和强行动(按钮),最大宽度 99px", + "第三列": "`ReactNode`", + "第四列": "-" + }, { "组件分类": "操作反馈", "组件名": "NoticeBar", @@ -6959,6 +6989,16 @@ "第三列": "`boolean`", "第四列": "false" }, + { + "组件分类": "操作反馈", + "组件名": "NoticeBar", + "版本号": "2.0.0", + "表格名": "Props", + "第一列": "autoClose", + "第二列": "自动关闭延时(毫秒),0 或不传为手动关闭", + "第三列": "`number`", + "第四列": "0" + }, { "组件分类": "操作反馈", "组件名": "NoticeBar", @@ -6975,7 +7015,7 @@ "版本号": "2.0.0", "表格名": "Props", "第一列": "rightIcon", - "第二列": "右边的 icon,在 closeable 模式下默认为 ``", + "第二列": "右边的 icon,在 closeable 模式下默认为 ``", "第三列": "`ReactNode`", "第四列": "-" }, @@ -6985,7 +7025,7 @@ "版本号": "2.0.0", "表格名": "Props", "第一列": "right", - "第二列": "区别于rightIcon,为右边自定义区域,仅用于 direction='horizontal' 模式", + "第二列": "~~已废弃,建议使用 action 替代~~ 右边自定义区域,仅用于 direction='horizontal' 模式", "第三列": "`ReactNode`", "第四列": "-" }, @@ -7199,6 +7239,16 @@ "第三列": "`onClose: () => void`", "第四列": "-" }, + { + "组件分类": "操作反馈", + "组件名": "Popover", + "版本号": "3.0.0", + "表格名": "Props", + "第一列": "type", + "第二列": "气泡类型,`status` 状态型(图标+文案+关闭),`description` 说明型(仅文案)", + "第三列": "`status` | `description`", + "第四列": "status" + }, { "组件分类": "操作反馈", "组件名": "Popover", @@ -7219,6 +7269,16 @@ "第三列": "`boolean`", "第四列": "false" }, + { + "组件分类": "操作反馈", + "组件名": "Popover", + "版本号": "3.0.0", + "表格名": "Props", + "第一列": "theme", + "第二列": "主题风格,默认 `dark` 为设计规范深色气泡;`light` 为明亮风格(白底深字)", + "第三列": "`light` | `dark`", + "第四列": "dark" + }, { "组件分类": "操作反馈", "组件名": "Popover", @@ -7237,7 +7297,7 @@ "第一列": "offset", "第二列": "出现位置的偏移量", "第三列": "`string[]` | `number[]`", - "第四列": "[0, 12]" + "第四列": "[0, 8]" }, { "组件分类": "操作反馈", @@ -7247,7 +7307,7 @@ "第一列": "arrowOffset", "第二列": "小箭头的偏移量", "第三列": "`number`", - "第四列": "0" + "第四列": "20" }, { "组件分类": "操作反馈", @@ -7279,6 +7339,26 @@ "第三列": "`boolean`", "第四列": "true" }, + { + "组件分类": "操作反馈", + "组件名": "Popover", + "版本号": "3.0.0", + "表格名": "Props", + "第一列": "autoShow", + "第二列": "是否自动弹出,需配合 `onOpen` 更新 `visible`", + "第三列": "`boolean`", + "第四列": "false" + }, + { + "组件分类": "操作反馈", + "组件名": "Popover", + "版本号": "3.0.0", + "表格名": "Props", + "第一列": "duration", + "第二列": "自动关闭时长(ms),`0` 表示不自动关闭", + "第三列": "`number`", + "第四列": "0" + }, { "组件分类": "操作反馈", "组件名": "Popover", @@ -7887,7 +7967,7 @@ "第一列": "visible", "第二列": "是否显示骨架屏(true不显示骨架屏,false显示骨架屏)", "第三列": "`boolean`", - "第四列": "true" + "第四列": "false" }, { "组件分类": "操作反馈", @@ -7897,7 +7977,7 @@ "第一列": "animated", "第二列": "是否开启骨架屏动画", "第三列": "`boolean`", - "第四列": "false" + "第四列": "true" }, { "组件分类": "操作反馈", @@ -7927,7 +8007,7 @@ "第一列": "duration", "第二列": "动画时长", "第三列": "`number`", - "第四列": "0.6" + "第四列": "0.4" }, { "组件分类": "操作反馈", diff --git a/src/sites/sites-react/doc/docs/ai-react/cli.en-US.md b/src/sites/sites-react/doc/docs/ai-react/cli.en-US.md index 6f57f34522..2e3596e64c 100644 --- a/src/sites/sites-react/doc/docs/ai-react/cli.en-US.md +++ b/src/sites/sites-react/doc/docs/ai-react/cli.en-US.md @@ -9,6 +9,7 @@ This guide explains how to use `@nutui/nutui-react-cli` to query NutUI-React com ## Highlights - **Fully offline, zero API key** — Component metadata, docs, and demos are bundled at build time. Once installed, queries run locally in milliseconds with no network requests and no latency. +- **Multi-version snapshots** — Bundles offline snapshots of multiple major versions (v3 / v4). Use `--nutui-version` to query the exact API of any version; when omitted, the CLI auto-detects the version your project uses. - **Structured output** — Every command supports `--format json` for agents to parse directly, instead of regex-matching text. - **Smart correction** — Component names are case-insensitive; typed `Buttn`? The CLI suggests `Button` based on edit distance rather than just erroring out. - **Bilingual docs** — The `doc` command supports `--lang zh|en` to switch between Chinese and English. @@ -44,6 +45,9 @@ nutui-react demo Button # List all H5 demo names for the component nutui-react demo Button demo1 # View a demo's source nutui-react token # Global Design Tokens nutui-react token Button # Component-level Design Tokens +nutui-react --nv 3.1.0 info Button # Query a specific NutUI version's API (auto-detected if omitted) +nutui-react migrate 3 4 --apply ./src # Scan a project and generate v3 → v4 migration guidance +nutui-react diff 3 4 Empty # Compare Empty props between v3 and v4 nutui-react mcp # Start a local MCP server for IDE integration ``` @@ -56,6 +60,8 @@ nutui-react mcp # Start a local MCP server for IDE integrat | `nutui-react doc [--lang zh\|en]` | Full component Markdown docs (Chinese by default) | | `nutui-react demo [name]` | Omit `name` to list all demos; pass `name` (e.g. `demo1`) for source | | `nutui-react token [Component]` | Design Tokens; omit the component name to list global tokens | +| `nutui-react migrate [from] [to]` | Print a major-version migration guide (defaults to `3 4`); use `--component ` for one component or `--apply ` to scan a project and generate migration guidance | +| `nutui-react diff [Component]` | Compare two version snapshots for added, removed, type-changed, and default-changed props | | `nutui-react mcp` | Start a local MCP server (stdio) for Claude Code / Cursor / VS Code / Codex IDE integration | When a component name isn't found, the CLI returns a "did you mean" suggestion (e.g. `Buttn` → `Button`) — use it to correct the name rather than guessing. @@ -65,13 +71,54 @@ When a component name isn't found, the CLI returns a "did you mean" suggestion ( | Flag | Description | Default | | --- | --- | --- | | `--format, -f ` | Output format; agents should prefer `json` | `text` | +| `--nutui-version, --nv ` | Target NutUI version (e.g. `3`, `3.1.0`, `4.0.0-beta.7`) | Auto-detected | | `--lang, -l ` | Doc language (for `doc` / `mcp`) | `zh` | | `--help, -h` | Show help | - | -| `--version, -v` | Print the CLI version | - | +| `--version, -v` | Print the CLI's own version (not the NutUI version) | - | + +## Multi-version + +The CLI bundles offline snapshots of multiple NutUI major versions (currently `v3.0.20`, `v3.1.0`, `v4.0.0-beta.7`), so it can answer with the right component knowledge for whichever NutUI version a machine uses. Pass `--nutui-version` (alias `--nv`) to target a version — `3`, `3.1.0`, `4.0.0-beta.7`, etc. all work: + +```bash +nutui-react --nv 3.1.0 info Button # Button props in 3.1.0 +nutui-react --nv 3 list # latest v3 snapshot +nutui-react --nv 4.0.0-beta.7 doc Cell +``` + +When `--nutui-version` is omitted, the target version is **auto-detected** in this order: + +1. the `--nutui-version ` flag; +2. the installed version in the project's `node_modules/@nutui/nutui-react/package.json`; +3. the `dependencies` / `devDependencies` / `peerDependencies` declaration in the project's `package.json` (handles `^3.1.0`, `~3.1.0`, etc.); +4. a fallback to the default major version (`v4`) latest. + +Version routing is `major.minor`-grained: a request for `3.0.5` lands on the highest patch snapshot of that minor series, `v3.0.20`; a minor higher than any available falls back to the nearest older minor of that major. Every query reports the resolved version and its source (the header in `text` output, the `_meta` field in `json` output). + +> When the version can be inferred from the project, prefer not passing `--nutui-version` and let the CLI detect it. The `-v / --version` semantics stay unchanged — it still prints the CLI's own version. + +## Migrate from v3 to v4 + +Both `migrate` and `diff` use offline data shipped with the CLI, but they answer different questions: + +- `migrate` reads the official migration guide and covers the reasoning and required edits, including styles, CSS class names, and Design Tokens that are not represented in props tables. +- `diff` compares two version snapshots and precisely reports added, removed, type-changed, and default-changed props. + +Scan the project first, then inspect each affected component: + +```bash +nutui-react migrate 3 4 --apply ./src --format json +nutui-react migrate 3 4 --component Empty --format json +nutui-react diff 3 4 Empty --format json +``` + +`--apply` only scans source files and outputs migration steps plus an agent prompt for the components actually used; it **does not modify files**. `matchedComponents` identifies components to update, while `componentsWithoutBreakingChanges` lists used components with no breaking change recorded in the current migration guide. + +Install the [nutui-react-v3-to-v4 Skill](/#/en-US/ai/skill) for an agent-guided workflow covering dependency upgrades, inventory, component-by-component edits, build checks, and visual verification. ## Usage with AI tools -The CLI ships with a Skill file following the [Agent Skills](https://github.com/vercel-labs/skills) spec, distributed with the npm package. It guides the agent to call the right command at the right time — e.g. "look up props with `info` and grab a demo before writing a component" and "customize styles with `var(--nutui-*)` tokens instead of hardcoded colors". +The CLI package ships with Skills that follow the [Agent Skills](https://github.com/vercel-labs/skills) spec. They cover both component development and v3 → v4 upgrades, guiding the agent to call the right command at the right time. Install into the current project (directly from the GitHub repo): diff --git a/src/sites/sites-react/doc/docs/ai-react/cli.md b/src/sites/sites-react/doc/docs/ai-react/cli.md index 77265cc12e..392f58f762 100644 --- a/src/sites/sites-react/doc/docs/ai-react/cli.md +++ b/src/sites/sites-react/doc/docs/ai-react/cli.md @@ -9,6 +9,7 @@ ## 亮点 - **完全离线、零 API Key** — 组件元数据与文档、示例在构建期打包进入,安装后本地毫秒级查询,无网络请求、无延迟。 +- **多版本快照** — 随包内置 v3 / v4 多个大版本的离线快照,`--nutui-version` 可查询任意版本的精确 API,未指定时自动检测项目所用版本。 - **结构化输出** — 所有命令支持 `--format json`,供 Agent 直接解析,而非正则抓文本。 - **智能纠错** — 组件名大小写不敏感;输入 `Buttn`?CLI 基于编辑距离建议 `Button`,而非直接报错。 - **双语文档** — `doc` 命令支持 `--lang zh|en` 切换中英文。 @@ -44,6 +45,9 @@ nutui-react demo Button # 列出组件的全部 H5 示例名 nutui-react demo Button demo1 # 查看某个示例的源码 nutui-react token # 全局 Design Token nutui-react token Button # 组件级 Design Token +nutui-react --nv 3.1.0 info Button # 查指定 NutUI 版本的 API(省略则自动检测) +nutui-react migrate 3 4 --apply ./src # 扫描项目并生成 v3 → v4 迁移提示 +nutui-react diff 3 4 Empty # 对比 Empty 在 v3 / v4 的 Props 差异 nutui-react mcp # 启动本地 MCP 服务,供 IDE 集成 ``` @@ -56,6 +60,8 @@ nutui-react mcp # 启动本地 MCP 服务,供 IDE 集成 | `nutui-react doc [--lang zh\|en]` | 组件完整 Markdown 文档,默认中文 | | `nutui-react demo [name]` | 省略 `name` 列出全部示例;指定 `name`(如 `demo1`)输出源码 | | `nutui-react token [Component]` | Design Token;省略组件名则列出全局 token | +| `nutui-react migrate [from] [to]` | 输出大版本迁移指南(默认 `3 4`);支持 `--component ` 查看单组件,或 `--apply ` 扫描项目并生成迁移提示 | +| `nutui-react diff [Component]` | 对比两个版本快照的 Props 差异,包括新增、移除、类型和默认值变更 | | `nutui-react mcp` | 启动本地 MCP 服务(stdio),供 Claude Code / Cursor / VS Code / Codex 等 IDE 集成 | 未命中组件名时,CLI 会给出「你是否想找」建议(如 `Buttn` → `Button`),据此纠正而非凭空猜测。 @@ -65,13 +71,54 @@ nutui-react mcp # 启动本地 MCP 服务,供 IDE 集成 | 参数 | 说明 | 默认值 | | --- | --- | --- | | `--format, -f ` | 输出格式;Agent 应优先用 `json` | `text` | +| `--nutui-version, --nv ` | 目标 NutUI 版本(如 `3`、`3.1.0`、`4.0.0-beta.7`) | 自动检测 | | `--lang, -l ` | 文档语言(用于 `doc` / `mcp`) | `zh` | | `--help, -h` | 显示帮助 | - | -| `--version, -v` | 打印 CLI 版本号 | - | +| `--version, -v` | 打印 CLI 自身版本号(非 NutUI 版本) | - | + +## 多版本 + +CLI 随包内置多个 NutUI 大版本的离线快照(当前:`v3.0.20`、`v3.1.0`、`v4.0.0-beta.7`),可对同一台机器上不同 NutUI 版本给出对应的组件知识。用 `--nutui-version`(别名 `--nv`)指定目标版本,支持 `3`、`3.1.0`、`4.0.0-beta.7` 等写法: + +```bash +nutui-react --nv 3.1.0 info Button # 查 3.1.0 的 Button Props +nutui-react --nv 3 list # 查 v3 最新快照 +nutui-react --nv 4.0.0-beta.7 doc Cell +``` + +未指定 `--nutui-version` 时,按以下顺序**自动检测**目标版本: + +1. `--nutui-version ` 显式指定; +2. 项目 `node_modules/@nutui/nutui-react/package.json` 的实际安装版本; +3. 项目 `package.json` 的 `dependencies` / `devDependencies` / `peerDependencies` 声明(兼容 `^3.1.0`、`~3.1.0` 等); +4. 兜底到默认大版本(`v4`)的 latest。 + +版本路由按 `major.minor` 粒度:请求 `3.0.5` 会落到该 minor 系列的最高 patch 快照 `v3.0.20`;请求的 minor 若高于已有,回退到该 major 最近的旧 minor。每次查询的输出都会标明实际命中的版本与来源(`text` 输出的头部、`json` 输出的 `_meta` 字段)。 + +> 在项目里能自动推断版本时,优先不显式传 `--nutui-version`,让 CLI 自行检测更省心。`-v / --version` 语义保持不变,仍输出 CLI 自身版本。 + +## v3 → v4 迁移 + +`migrate` 与 `diff` 都基于随 CLI 分发的离线数据,但用途不同: + +- `migrate` 读取官方迁移文档,说明为什么改、怎么改,并覆盖样式、CSS 类名和 Design Token 等 Props 表之外的变化。 +- `diff` 实时比较两个版本快照,精确列出 Props 的新增、移除、类型和默认值变化。 + +推荐先扫描项目,再逐组件核对: + +```bash +nutui-react migrate 3 4 --apply ./src --format json +nutui-react migrate 3 4 --component Empty --format json +nutui-react diff 3 4 Empty --format json +``` + +`--apply` 只扫描源码并输出项目实际使用组件的迁移步骤与 Agent 提示,**不会自动修改文件**。输出中的 `matchedComponents` 是需要重点处理的组件,`componentsWithoutBreakingChanges` 是已使用但当前迁移文档未记录破坏性变更的组件。 + +完整升级流程可安装 [nutui-react-v3-to-v4 Skill](/#/zh-CN/ai/skill),由 Agent 按「升级依赖 → 扫描盘点 → 逐组件改写 → 构建与视觉验证」执行。 ## 在 AI 工具中使用 -CLI 内置一份遵循 [Agent Skills](https://github.com/vercel-labs/skills) 规范的 Skill 文件,随 npm 包分发,指导 Agent 在正确的时机调用正确的命令——例如「写组件前先 `info` 查 Props、再 `demo` 拿示例」「定制样式用 `var(--nutui-*)` token 而非硬编码颜色」。 +CLI 随 npm 包分发遵循 [Agent Skills](https://github.com/vercel-labs/skills) 规范的 Skill,分别覆盖组件开发与 v3 → v4 升级流程,指导 Agent 在正确的时机调用正确的命令。 安装到当前项目(从 GitHub 仓库直接安装): diff --git a/src/sites/sites-react/doc/docs/ai-react/mcp.en-US.md b/src/sites/sites-react/doc/docs/ai-react/mcp.en-US.md index 88be6273a6..c24d08624d 100644 --- a/src/sites/sites-react/doc/docs/ai-react/mcp.en-US.md +++ b/src/sites/sites-react/doc/docs/ai-react/mcp.en-US.md @@ -10,7 +10,7 @@ The CLI is "the agent runs commands"; MCP registers the same capabilities as IDE ## Start the MCP server -`nutui-react mcp` starts a local MCP server that communicates over stdio, exposing 5 read-only tools and 2 prompts. This command shouldn't be run directly in a terminal — configure it in your AI tool instead (see below). +`nutui-react mcp` starts a local MCP server that communicates over stdio, exposing 7 read-only tools and 2 prompts. This command shouldn't be run directly in a terminal — configure it in your AI tool instead (see below). ### Tools @@ -21,9 +21,15 @@ The CLI is "the agent runs commands"; MCP registers the same capabilities as IDE | `nutui_doc` | Get a component's full docs (`lang: zh\|en`) | | `nutui_demo` | Get a component's H5 demo list / source | | `nutui_token` | Query Design Tokens (global / component-level) | +| `nutui_migrate` | Get a major-version migration guide, optionally filtered by component or project directory | +| `nutui_diff` | Compare prop differences between two version snapshots | All tools are read-only, side-effect-free, and do not access the external network. +The first five component-knowledge tools accept an optional `nutuiVersion` argument (e.g. `3.1.0`, `4.0.0-beta.7`) to target a NutUI major version; when omitted, the version your project uses is auto-detected, falling back to the default major. See the [CLI multi-version notes](/#/en-US/ai/cli). + +`nutui_migrate` accepts optional `from`, `to`, `component`, and `applyDir` arguments. `applyDir` only scans the directory and returns migration steps; it does not modify files. `nutui_diff` requires `v1` and `v2`, with an optional `component` filter. They return the same structured data as the CLI's `migrate` and `diff` commands for agent-driven v3 → v4 upgrades. + ### Prompts | Prompt | Description | diff --git a/src/sites/sites-react/doc/docs/ai-react/mcp.md b/src/sites/sites-react/doc/docs/ai-react/mcp.md index f153dd4e05..054a3d8b6f 100644 --- a/src/sites/sites-react/doc/docs/ai-react/mcp.md +++ b/src/sites/sites-react/doc/docs/ai-react/mcp.md @@ -10,7 +10,7 @@ CLI 是「Agent 主动敲命令」,MCP 则把同一份能力注册成 IDE 原 ## 启动 MCP 服务 -`nutui-react mcp` 启动一个通过 stdio 通信的本地 MCP 服务,暴露 5 个只读工具与 2 个提示词。该命令不应在终端直接裸跑,而应在 AI 工具中配置(见下方)。 +`nutui-react mcp` 启动一个通过 stdio 通信的本地 MCP 服务,暴露 7 个只读工具与 2 个提示词。该命令不应在终端直接裸跑,而应在 AI 工具中配置(见下方)。 ### 工具 @@ -21,9 +21,16 @@ CLI 是「Agent 主动敲命令」,MCP 则把同一份能力注册成 IDE 原 | `nutui_doc` | 获取组件完整文档(`lang: zh\|en`) | | `nutui_demo` | 获取组件 H5 示例列表 / 源码 | | `nutui_token` | 查询 Design Token(全局 / 组件级) | +| `nutui_migrate` | 获取大版本迁移指南;可按组件筛选或扫描项目目录 | +| `nutui_diff` | 对比两个版本快照的 Props 差异 | 所有工具均为只读、无副作用、不访问外部网络。 +前 5 个组件知识工具都接受可选入参 `nutuiVersion`(如 `3.1.0`、`4.0.0-beta.7`),用于指定目标 NutUI 大版本;省略时自动检测项目所用版本,检测不到则回退默认大版本。详见 [CLI 的「多版本」说明](/#/zh-CN/ai/cli)。 + +- `nutui_migrate` 接受可选的 `from`、`to`、`component` 和 `applyDir` 参数; +- `applyDir` 只扫描目录并返回迁移步骤,不修改文件。`nutui_diff` 要求传入 `v1`、`v2`,也可用 `component` 限定单个组件。两者与 CLI 的 `migrate`、`diff` 命令返回同类结构化数据,可由 Agent 直接用于 v3 → v4 升级。 + ### 提示词 | 提示词 | 说明 | diff --git a/src/sites/sites-react/doc/docs/ai-react/skill.en-US.md b/src/sites/sites-react/doc/docs/ai-react/skill.en-US.md index d104730b3e..4a974b706b 100644 --- a/src/sites/sites-react/doc/docs/ai-react/skill.en-US.md +++ b/src/sites/sites-react/doc/docs/ai-react/skill.en-US.md @@ -1,6 +1,6 @@ # Skill -This page introduces two NutUI-React Agent Skills, the problem each one solves, and how to install them. +This page introduces three NutUI-React Agent Skills, the problem each one solves, and how to install them. ## What is a Skill? @@ -8,14 +8,15 @@ A [Skill](https://github.com/vercel-labs/skills) is an instruction file followin Once installed, the agent automatically loads the Skill and follows its workflow when it hits a matching task, with no need for you to prompt it every time. Compatible with Claude Code / Cursor / VS Code / Codex and any agent that supports the skills protocol. -Two Skills are available: +Three Skills are available: | Skill | Purpose | | --- | --- | | `nutui-react` | Look up the API before writing NutUI-React code, eliminating API hallucination | +| `nutui-react-v3-to-v4` | Systematically upgrade `@nutui/nutui-react` from v3 to v4 | | `nutui-react-to-taro` | Migrate an H5 project from `@nutui/nutui-react` to `@nutui/nutui-react-taro` (Taro / mini-program) | -## `nutui-react` — look up before writing +## nutui-react — look up before writing Guides the agent to query the real API with the CLI before writing any NutUI-React component code, instead of guessing props or enum values from memory. @@ -32,7 +33,25 @@ Guides the agent to query the real API with the CLI before writing any NutUI-Rea npx skills add jdf2e/nutui-react --skill nutui-react ``` -## `nutui-react-to-taro` — migrate H5 to Taro +## nutui-react-v3-to-v4 — major-version upgrade + +Guides the agent through upgrading `@nutui/nutui-react` from v3 to v4 with the new `migrate` and `diff` capabilities. It focuses on style, CSS class, Design Token, enum, and default-value changes that may not produce compile errors. + +**Workflow:** + +- Ask for confirmation before upgrading dependencies, then verify the lockfile and installation result. +- Run `nutui-react migrate 3 4 --apply ./src --format json` to inventory the components actually used by the project. +- For every matched component, combine `migrate --component` with `diff 3 4` to check both the official migration guidance and exact prop changes. +- Inspect custom styles targeting `.nut-*` classes and `--nutui-*` variables. +- Validate builds, rendering, and interactions; a successful compilation alone does not complete the migration. + +**Install:** + +```bash +npx skills add jdf2e/nutui-react --skill nutui-react-v3-to-v4 +``` + +## nutui-react-to-taro — migrate H5 to Taro Guides the agent to migrate a project using `@nutui/nutui-react` (H5) to `@nutui/nutui-react-taro` (Taro cross-platform / mini-program). The two packages share one component set and almost every component maps 1:1, so migration is highly regular — the Skill orchestrates the mechanical rewrites and the judgment-heavy semantic rewrites into a clear workflow. @@ -55,8 +74,8 @@ npx skills add jdf2e/nutui-react --skill nutui-react-to-taro All three reuse the same offline knowledge — only the calling protocol differs: -- **CLI** — the agent runs commands to query. -- **MCP** — registers the same capabilities as IDE-native tools, called on demand in conversation. +- **CLI** — the agent runs commands to query knowledge, scan a project, or compare versions. +- **MCP** — registers the same query, migration, and diff capabilities as IDE-native tools, called on demand in conversation. - **Skill** — provides no new capability; it tells the agent _when and in what order_ to call the CLI / MCP, codifying the workflow. If your IDE supports MCP, enabling the MCP server alongside lets the Skill's query steps route through IDE-native tools automatically. diff --git a/src/sites/sites-react/doc/docs/ai-react/skill.md b/src/sites/sites-react/doc/docs/ai-react/skill.md index 432d4acb1e..ecec019f6e 100644 --- a/src/sites/sites-react/doc/docs/ai-react/skill.md +++ b/src/sites/sites-react/doc/docs/ai-react/skill.md @@ -1,6 +1,6 @@ # Skill -本篇介绍两个 NutUI-React Agent Skill,以及它们各自解决的问题与安装方式。 +本篇介绍三个 NutUI-React Agent Skill,以及它们各自解决的问题与安装方式。 ## 什么是 Skill? @@ -8,14 +8,15 @@ 安装后,Agent 在遇到对应任务时会自动加载 Skill 并遵循其中的流程,无需你每次手动提示。兼容 Claude Code / Cursor / VS Code / Codex 等所有支持 skills 协议的 Agent。 -目前提供两个 Skill: +目前提供三个 Skill: | Skill | 用途 | | --- | --- | | `nutui-react` | 写 NutUI-React 代码时「先查后写」,消除 API 幻觉 | +| `nutui-react-v3-to-v4` | 将 `@nutui/nutui-react` 从 v3 系统升级到 v4 | | `nutui-react-to-taro` | 把 H5 项目从 `@nutui/nutui-react` 迁移到 `@nutui/nutui-react-taro`(Taro / 小程序) | -## `nutui-react` — 先查后写 +## nutui-react — 先查后写 指导 Agent 在写任何 NutUI-React 组件代码前,先用 CLI 查询真实 API,而非凭记忆猜测 Prop 或枚举值。 @@ -32,7 +33,25 @@ npx skills add jdf2e/nutui-react --skill nutui-react ``` -## `nutui-react-to-taro` — H5 迁移到 Taro +## nutui-react-v3-to-v4 — 大版本升级 + +指导 Agent 使用 `migrate` 与 `diff` 能力,把 `@nutui/nutui-react` 从 v3 升级到 v4。 + +**它编排的迁移流程:** + +- 升级依赖前先征求用户确认,并核对锁文件与安装结果。 +- 用 `nutui-react migrate 3 4 --apply ./src --format json` 扫描项目,建立实际使用组件的迁移清单。 +- 对每个命中的组件结合 `migrate --component` 与 `diff 3 4`,同时核对官方迁移说明和 Props 精确差异。 +- 检查项目中针对 `.nut-*` 类名与 `--nutui-*` 变量的自定义样式覆盖。 +- 完成构建与视觉、交互验证;编译通过不等于迁移完成。 + +**安装:** + +```bash +npx skills add jdf2e/nutui-react --skill nutui-react-v3-to-v4 +``` + +## nutui-react-to-taro — H5 迁移到 Taro 指导 Agent 把使用 `@nutui/nutui-react`(H5)的项目,迁移到 `@nutui/nutui-react-taro`(Taro 跨端 / 小程序)。两个包共用同一套组件,绝大多数组件一一对应,因此迁移高度规则化——Skill 负责把规则化改写与需要判断的语义改写编排成清晰流程。 @@ -55,8 +74,8 @@ npx skills add jdf2e/nutui-react --skill nutui-react-to-taro 三者复用同一份离线知识,只是调用协议不同: -- **CLI** — Agent 主动敲命令查询。 -- **MCP** — 把同一份能力注册成 IDE 原生工具,在对话中按需自动调用。 +- **CLI** — Agent 主动运行命令查询知识、扫描项目或比较版本。 +- **MCP** — 把同一份查询、迁移和差异对比能力注册成 IDE 原生工具,在对话中按需自动调用。 - **Skill** — 不提供新能力,而是告诉 Agent「何时、按什么顺序」调用 CLI / MCP,把流程固化下来。 如果你的 IDE 支持 MCP,推荐同时启用 MCP 服务,让 Skill 里的查询步骤自动走 IDE 原生工具。 diff --git a/src/sites/sites-react/doc/docs/ai-taro/cli.en-US.md b/src/sites/sites-react/doc/docs/ai-taro/cli.en-US.md index bcf7e77e5f..0fe6468486 100644 --- a/src/sites/sites-react/doc/docs/ai-taro/cli.en-US.md +++ b/src/sites/sites-react/doc/docs/ai-taro/cli.en-US.md @@ -11,6 +11,7 @@ It serves `@nutui/nutui-react-taro` (Taro cross-platform / mini-program). For H5 ## Highlights - **Fully offline, zero API key** — Component metadata, docs, and demos are bundled at build time. Once installed, queries run locally in milliseconds with no network requests and no latency. +- **Multi-version snapshots** — Bundles offline snapshots of multiple major versions (v3 / v4). Use `--nutui-version` to query the exact API of any version; when omitted, the CLI auto-detects the version your project uses. - **Structured output** — Every command supports `--format json` for agents to parse directly, instead of regex-matching text. - **Smart correction** — Component names are case-insensitive; typed `Buttn`? The CLI suggests `Button` based on edit distance rather than just erroring out. - **Taro-specific data** — Props and demos come from the Taro-side docs (`doc.taro.md` / `demos/taro`), which may differ from the H5 package. @@ -45,6 +46,9 @@ nutui-react-taro demo Button # List all Taro demo names for the com nutui-react-taro demo Button demo1 # View a demo's source nutui-react-taro token # Global Design Tokens nutui-react-taro token Button # Component-level Design Tokens +nutui-react-taro --nv 3.1.0 info Button # Query a specific NutUI version's API (auto-detected if omitted) +nutui-react-taro migrate 3 4 --apply ./src # Scan a project and generate v3 → v4 migration guidance +nutui-react-taro diff 3 4 Empty # Compare Empty props between v3 and v4 nutui-react-taro mcp # Start a local MCP server for IDE integration ``` @@ -57,6 +61,8 @@ nutui-react-taro mcp # Start a local MCP server for IDE int | `nutui-react-taro doc ` | Full component Markdown docs (Chinese) | | `nutui-react-taro demo [name]` | Omit `name` to list all demos; pass `name` (e.g. `demo1`) for source | | `nutui-react-taro token [Component]` | Design Tokens; omit the component name to list global tokens | +| `nutui-react-taro migrate [from] [to]` | Print a major-version migration guide (defaults to `3 4`); use `--component ` for one component or `--apply ` to scan a project and generate migration guidance | +| `nutui-react-taro diff [Component]` | Compare two version snapshots for added, removed, type-changed, and default-changed props | | `nutui-react-taro mcp` | Start a local MCP server (stdio) for Claude Code / Cursor / VS Code / Codex IDE integration | When a component name isn't found, the CLI returns a "did you mean" suggestion (e.g. `Buttn` → `Button`) — use it to correct the name rather than guessing. @@ -66,12 +72,53 @@ When a component name isn't found, the CLI returns a "did you mean" suggestion ( | Flag | Description | Default | | --- | --- | --- | | `--format, -f ` | Output format; agents should prefer `json` | `text` | +| `--nutui-version, --nv ` | Target NutUI version (e.g. `3`, `3.1.0`, `4.0.0-beta.7`) | Auto-detected | | `--help, -h` | Show help | - | -| `--version, -v` | Print the CLI version | - | +| `--version, -v` | Print the CLI's own version (not the NutUI version) | - | + +## Multi-version + +The CLI bundles offline snapshots of multiple NutUI major versions (currently `v3.0.20`, `v3.1.0`, `v4.0.0-beta.7`), so it can answer with the right component knowledge for whichever NutUI version a machine uses. Pass `--nutui-version` (alias `--nv`) to target a version — `3`, `3.1.0`, `4.0.0-beta.7`, etc. all work: + +```bash +nutui-react-taro --nv 3.1.0 info Button +nutui-react-taro --nv 3 list +nutui-react-taro --nv 4.0.0-beta.7 doc Cell +``` + +When `--nutui-version` is omitted, the target version is **auto-detected** in this order: + +1. the `--nutui-version ` flag; +2. the installed version in the project's `node_modules/@nutui/nutui-react-taro/package.json`; +3. the `dependencies` / `devDependencies` / `peerDependencies` declaration in the project's `package.json` (handles `^3.1.0`, `~3.1.0`, etc.); +4. a fallback to the default major version (`v4`) latest. + +Version routing is `major.minor`-grained: a request for `3.0.5` lands on the highest patch snapshot of that minor series, `v3.0.20`; a minor higher than any available falls back to the nearest older minor of that major. Every query reports the resolved version and its source (the header in `text` output, the `_meta` field in `json` output). + +> When the version can be inferred from the project, prefer not passing `--nutui-version` and let the CLI detect it. The `-v / --version` semantics stay unchanged — it still prints the CLI's own version. + +## Migrate from v3 to v4 + +Both `migrate` and `diff` use offline data shipped with the CLI, but they answer different questions: + +- `migrate` reads the official migration guide and covers the reasoning and required edits, including styles, CSS class names, and Design Tokens that are not represented in props tables. +- `diff` compares two version snapshots and precisely reports added, removed, type-changed, and default-changed props. + +Scan the project first, then inspect each affected component: + +```bash +nutui-react-taro migrate 3 4 --apply ./src --format json +nutui-react-taro migrate 3 4 --component Empty --format json +nutui-react-taro diff 3 4 Empty --format json +``` + +`--apply` only scans source files and outputs migration steps plus an agent prompt for the components actually used; it **does not modify files**. `matchedComponents` identifies components to update, while `componentsWithoutBreakingChanges` lists used components with no breaking change recorded in the current migration guide. + +Install the [nutui-react-taro-v3-to-v4 Skill](/#/en-US/ai/skill) for an agent-guided workflow covering dependency upgrades, inventory, component-by-component edits, multi-platform builds, and visual verification. ## Usage with AI tools -The CLI ships with a Skill file following the [Agent Skills](https://github.com/vercel-labs/skills) spec, distributed with the npm package. It guides the agent to call the right command at the right time — e.g. "look up props with `info` and grab a demo before writing a component" and "customize styles with `var(--nutui-*)` tokens instead of hardcoded colors". +The CLI package ships with Skills that follow the [Agent Skills](https://github.com/vercel-labs/skills) spec. They cover both component development and v3 → v4 upgrades, guiding the agent to call the right command at the right time. Install into the current project (directly from the GitHub repo): @@ -85,6 +132,7 @@ If your IDE supports MCP, the CLI can also run as an MCP server, registering the ## Learn more +- [Skill](/#/en-US/ai/skill) - [MCP Server](/#/en-US/ai/mcp) - [LLMs.txt](/#/en-US/ai/llms) - [For Agents](/#/en-US/ai/for-agents) diff --git a/src/sites/sites-react/doc/docs/ai-taro/cli.md b/src/sites/sites-react/doc/docs/ai-taro/cli.md index 9914decb53..efb850ddb4 100644 --- a/src/sites/sites-react/doc/docs/ai-taro/cli.md +++ b/src/sites/sites-react/doc/docs/ai-taro/cli.md @@ -11,6 +11,7 @@ ## 亮点 - **完全离线、零 API Key** — 组件元数据与文档、示例在构建期打包进入,安装后本地毫秒级查询,无网络请求、无延迟。 +- **多版本快照** — 随包内置 v3 / v4 多个大版本的离线快照,`--nutui-version` 可查询任意版本的精确 API,未指定时自动检测项目所用版本。 - **结构化输出** — 所有命令支持 `--format json`,供 Agent 直接解析,而非正则抓文本。 - **智能纠错** — 组件名大小写不敏感;输入 `Buttn`?CLI 基于编辑距离建议 `Button`,而非直接报错。 - **Taro 端专属数据** — Props 与示例取自 Taro 端文档(`doc.taro.md` / `demos/taro`),与 H5 包可能不同。 @@ -45,6 +46,9 @@ nutui-react-taro demo Button # 列出组件的全部 Taro 示例名 nutui-react-taro demo Button demo1 # 查看某个示例的源码 nutui-react-taro token # 全局 Design Token nutui-react-taro token Button # 组件级 Design Token +nutui-react-taro --nv 3.1.0 info Button # 查指定 NutUI 版本的 API(省略则自动检测) +nutui-react-taro migrate 3 4 --apply ./src # 扫描项目并生成 v3 → v4 迁移提示 +nutui-react-taro diff 3 4 Empty # 对比 Empty 在 v3 / v4 的 Props 差异 nutui-react-taro mcp # 启动本地 MCP 服务,供 IDE 集成 ``` @@ -57,6 +61,8 @@ nutui-react-taro mcp # 启动本地 MCP 服务,供 IDE | `nutui-react-taro doc ` | 组件完整 Markdown 文档(中文) | | `nutui-react-taro demo [name]` | 省略 `name` 列出全部示例;指定 `name`(如 `demo1`)输出源码 | | `nutui-react-taro token [Component]` | Design Token;省略组件名则列出全局 token | +| `nutui-react-taro migrate [from] [to]` | 输出大版本迁移指南(默认 `3 4`);支持 `--component ` 查看单组件,或 `--apply ` 扫描项目并生成迁移提示 | +| `nutui-react-taro diff [Component]` | 对比两个版本快照的 Props 差异,包括新增、移除、类型和默认值变更 | | `nutui-react-taro mcp` | 启动本地 MCP 服务(stdio),供 Claude Code / Cursor / VS Code / Codex 等 IDE 集成 | 未命中组件名时,CLI 会给出「你是否想找」建议(如 `Buttn` → `Button`),据此纠正而非凭空猜测。 @@ -66,12 +72,53 @@ nutui-react-taro mcp # 启动本地 MCP 服务,供 IDE | 参数 | 说明 | 默认值 | | --- | --- | --- | | `--format, -f ` | 输出格式;Agent 应优先用 `json` | `text` | +| `--nutui-version, --nv ` | 目标 NutUI 版本(如 `3`、`3.1.0`、`4.0.0-beta.7`) | 自动检测 | | `--help, -h` | 显示帮助 | - | -| `--version, -v` | 打印 CLI 版本号 | - | +| `--version, -v` | 打印 CLI 自身版本号(非 NutUI 版本) | - | + +## 多版本 + +CLI 随包内置多个 NutUI 大版本的离线快照(当前:`v3.0.20`、`v3.1.0`、`v4.0.0-beta.7`),可对同一台机器上不同 NutUI 版本给出对应的组件知识。用 `--nutui-version`(别名 `--nv`)指定目标版本,支持 `3`、`3.1.0`、`4.0.0-beta.7` 等写法: + +```bash +nutui-react-taro --nv 3.1.0 info Button +nutui-react-taro --nv 3 list +nutui-react-taro --nv 4.0.0-beta.7 doc Cell +``` + +未指定 `--nutui-version` 时,按以下顺序**自动检测**目标版本: + +1. `--nutui-version ` 显式指定; +2. 项目 `node_modules/@nutui/nutui-react-taro/package.json` 的实际安装版本; +3. 项目 `package.json` 的 `dependencies` / `devDependencies` / `peerDependencies` 声明(兼容 `^3.1.0`、`~3.1.0` 等); +4. 兜底到默认大版本(`v4`)的 latest。 + +版本路由按 `major.minor` 粒度:请求 `3.0.5` 会落到该 minor 系列的最高 patch 快照 `v3.0.20`;请求的 minor 若高于已有,回退到该 major 最近的旧 minor。每次查询的输出都会标明实际命中的版本与来源(`text` 输出的头部、`json` 输出的 `_meta` 字段)。 + +> 在项目里能自动推断版本时,优先不显式传 `--nutui-version`,让 CLI 自行检测更省心。`-v / --version` 语义保持不变,仍输出 CLI 自身版本。 + +## v3 → v4 迁移 + +`migrate` 与 `diff` 都基于随 CLI 分发的离线数据,但用途不同: + +- `migrate` 读取官方迁移文档,说明为什么改、怎么改,并覆盖样式、CSS 类名和 Design Token 等 Props 表之外的变化。 +- `diff` 实时比较两个版本快照,精确列出 Props 的新增、移除、类型和默认值变化。 + +推荐先扫描项目,再逐组件核对: + +```bash +nutui-react-taro migrate 3 4 --apply ./src --format json +nutui-react-taro migrate 3 4 --component Empty --format json +nutui-react-taro diff 3 4 Empty --format json +``` + +`--apply` 只扫描源码并输出项目实际使用组件的迁移步骤与 Agent 提示,**不会自动修改文件**。输出中的 `matchedComponents` 是需要重点处理的组件,`componentsWithoutBreakingChanges` 是已使用但当前迁移文档未记录破坏性变更的组件。 + +完整升级流程可安装 [nutui-react-taro-v3-to-v4 Skill](/#/zh-CN/ai/skill),由 Agent 按「升级依赖 → 扫描盘点 → 逐组件改写 → 多端构建与视觉验证」执行。 ## 在 AI 工具中使用 -CLI 内置一份遵循 [Agent Skills](https://github.com/vercel-labs/skills) 规范的 Skill 文件,随 npm 包分发,指导 Agent 在正确的时机调用正确的命令——例如「写组件前先 `info` 查 Props、再 `demo` 拿示例」「定制样式用 `var(--nutui-*)` token 而非硬编码颜色」。 +CLI 随 npm 包分发遵循 [Agent Skills](https://github.com/vercel-labs/skills) 规范的 Skill,分别覆盖组件开发与 v3 → v4 升级流程,指导 Agent 在正确的时机调用正确的命令。 安装到当前项目(从 GitHub 仓库直接安装): @@ -85,6 +132,7 @@ npx skills add jdf2e/nutui-react --skill nutui-react-taro ## 了解更多 +- [Skill](/#/zh-CN/ai/skill) - [MCP Server](/#/zh-CN/ai/mcp) - [LLMs.txt](/#/zh-CN/ai/llms) - [For Agents](/#/zh-CN/ai/for-agents) diff --git a/src/sites/sites-react/doc/docs/ai-taro/mcp.en-US.md b/src/sites/sites-react/doc/docs/ai-taro/mcp.en-US.md index 7954f590df..603cc09257 100644 --- a/src/sites/sites-react/doc/docs/ai-taro/mcp.en-US.md +++ b/src/sites/sites-react/doc/docs/ai-taro/mcp.en-US.md @@ -10,7 +10,7 @@ The CLI is "the agent runs commands"; MCP registers the same capabilities as IDE ## Start the MCP server -`nutui-react-taro mcp` starts a local MCP server that communicates over stdio, exposing 5 read-only tools and 2 prompts. This command shouldn't be run directly in a terminal — configure it in your AI tool instead (see below). +`nutui-react-taro mcp` starts a local MCP server that communicates over stdio, exposing 7 read-only tools and 2 prompts. This command shouldn't be run directly in a terminal — configure it in your AI tool instead (see below). ### Tools @@ -21,9 +21,15 @@ The CLI is "the agent runs commands"; MCP registers the same capabilities as IDE | `nutui_doc` | Get a component's full docs (Chinese) | | `nutui_demo` | Get a component's Taro demo list / source | | `nutui_token` | Query Design Tokens (global / component-level) | +| `nutui_migrate` | Get a major-version migration guide, optionally filtered by component or project directory | +| `nutui_diff` | Compare prop differences between two version snapshots | All tools are read-only, side-effect-free, and do not access the external network. +The first five component-knowledge tools accept an optional `nutuiVersion` argument (e.g. `3.1.0`, `4.0.0-beta.7`) to target a NutUI major version; when omitted, the version your project uses is auto-detected, falling back to the default major. See the [CLI multi-version notes](/#/en-US/ai/cli). + +`nutui_migrate` accepts optional `from`, `to`, `component`, and `applyDir` arguments. `applyDir` only scans the directory and returns migration steps; it does not modify files. `nutui_diff` requires `v1` and `v2`, with an optional `component` filter. They return the same structured data as the CLI's `migrate` and `diff` commands for agent-driven v3 → v4 upgrades. + ### Prompts | Prompt | Description | diff --git a/src/sites/sites-react/doc/docs/ai-taro/mcp.md b/src/sites/sites-react/doc/docs/ai-taro/mcp.md index fe692f461d..033e54532c 100644 --- a/src/sites/sites-react/doc/docs/ai-taro/mcp.md +++ b/src/sites/sites-react/doc/docs/ai-taro/mcp.md @@ -10,7 +10,7 @@ CLI 是「Agent 主动敲命令」,MCP 则把同一份能力注册成 IDE 原 ## 启动 MCP 服务 -`nutui-react-taro mcp` 启动一个通过 stdio 通信的本地 MCP 服务,暴露 5 个只读工具与 2 个提示词。该命令不应在终端直接裸跑,而应在 AI 工具中配置(见下方)。 +`nutui-react-taro mcp` 启动一个通过 stdio 通信的本地 MCP 服务,暴露 7 个只读工具与 2 个提示词。该命令不应在终端直接裸跑,而应在 AI 工具中配置(见下方)。 ### 工具 @@ -21,9 +21,16 @@ CLI 是「Agent 主动敲命令」,MCP 则把同一份能力注册成 IDE 原 | `nutui_doc` | 获取组件完整文档(中文) | | `nutui_demo` | 获取组件 Taro 示例列表 / 源码 | | `nutui_token` | 查询 Design Token(全局 / 组件级) | +| `nutui_migrate` | 获取大版本迁移指南;可按组件筛选或扫描项目目录 | +| `nutui_diff` | 对比两个版本快照的 Props 差异 | 所有工具均为只读、无副作用、不访问外部网络。 +前 5 个组件知识工具都接受可选入参 `nutuiVersion`(如 `3.1.0`、`4.0.0-beta.7`),用于指定目标 NutUI 大版本;省略时自动检测项目所用版本,检测不到则回退默认大版本。详见 [CLI 的「多版本」说明](/#/zh-CN/ai/cli)。 + +- `nutui_migrate` 接受可选的 `from`、`to`、`component` 和 `applyDir` 参数; +- `applyDir` 只扫描目录并返回迁移步骤,不修改文件。`nutui_diff` 要求传入 `v1`、`v2`,也可用 `component` 限定单个组件。两者与 CLI 的 `migrate`、`diff` 命令返回同类结构化数据,可由 Agent 直接用于 v3 → v4 升级。 + ### 提示词 | 提示词 | 说明 | diff --git a/src/sites/sites-react/doc/docs/ai-taro/skill.en-US.md b/src/sites/sites-react/doc/docs/ai-taro/skill.en-US.md new file mode 100644 index 0000000000..56a94cd916 --- /dev/null +++ b/src/sites/sites-react/doc/docs/ai-taro/skill.en-US.md @@ -0,0 +1,63 @@ +# Skill + +This page introduces the two NutUI-React Taro Agent Skills, the problem each one solves, and how to install them. + +## What is a Skill? + +A [Skill](https://github.com/vercel-labs/skills) is an instruction file following the Agent Skills spec. The CLI and MCP provide query, migration, and diff capabilities, while a Skill defines when and in what order the agent should use them. + +Two Skills are available: + +| Skill | Purpose | +| --- | --- | +| `nutui-react-taro` | Look up the API before writing NutUI-React Taro code, eliminating API hallucination | +| `nutui-react-taro-v3-to-v4` | Systematically upgrade `@nutui/nutui-react-taro` from v3 to v4 | + +## nutui-react-taro — look up before writing + +Guides the agent to query real APIs and Taro demos through the CLI or MCP before writing NutUI-React Taro code, rather than reusing H5 APIs or guessing from memory. + +**Behavior it enforces:** + +- Run `nutui-react-taro info ` for props and `demo` for a Taro example before writing component code. +- Use `doc` for complete documentation and `token` for `var(--nutui-*)` Design Tokens. +- Confirm uncertain component names with `list`, and prefer structured `--format json` output. + +**Install:** + +```bash +npx skills add jdf2e/nutui-react --skill nutui-react-taro +``` + +## nutui-react-taro-v3-to-v4 — major-version upgrade + +Guides the agent through upgrading `@nutui/nutui-react-taro` from v3 to v4 with the new `migrate` and `diff` capabilities. It focuses on style, CSS class, Design Token, enum, and default-value changes that may not produce compile errors. + +**Workflow:** + +- Ask for confirmation before upgrading dependencies, then verify the lockfile and installation result. +- Run `nutui-react-taro migrate 3 4 --apply ./src --format json` to inventory the components actually used by the project. +- For every matched component, combine `migrate --component` with `diff 3 4` to check both the official migration guidance and exact prop changes. +- Inspect custom styles targeting `.nut-*` classes and `--nutui-*` variables. +- Validate builds, rendering, and interactions on the target mini-program, H5, or other Taro platforms; successful compilation alone does not complete the migration. + +**Install:** + +```bash +npx skills add jdf2e/nutui-react --skill nutui-react-taro-v3-to-v4 +``` + +## How Skill, CLI, and MCP relate + +- **CLI** — The agent runs commands to query knowledge, scan a project, or compare versions. +- **MCP** — Registers the same query, migration, and diff capabilities as IDE-native tools for automatic, on-demand calls in conversation. +- **Skill** — Defines task triggers, execution order, change boundaries, and verification requirements. + +If your IDE supports MCP, enable the MCP server as well so the Skill's query and migration steps can call IDE-native tools directly. + +## Learn more + +- [CLI](/#/en-US/ai/cli) +- [MCP Server](/#/en-US/ai/mcp) +- [For Agents](/#/en-US/ai/for-agents) +- [LLMs.txt](/#/en-US/ai/llms) diff --git a/src/sites/sites-react/doc/docs/ai-taro/skill.md b/src/sites/sites-react/doc/docs/ai-taro/skill.md new file mode 100644 index 0000000000..614e263c8f --- /dev/null +++ b/src/sites/sites-react/doc/docs/ai-taro/skill.md @@ -0,0 +1,63 @@ +# Skill + +本篇介绍 NutUI-React Taro 的两个 Agent Skill,以及它们各自解决的问题与安装方式。 + +## 什么是 Skill? + +[Skill](https://github.com/vercel-labs/skills) 是一份遵循 Agent Skills 规范的说明文件。CLI 与 MCP 提供查询、迁移和差异对比能力,Skill 则约束 Agent 在什么时机、按什么顺序使用这些能力。 + +目前提供两个 Skill: + +| Skill | 用途 | +| --- | --- | +| `nutui-react-taro` | 写 NutUI-React Taro 代码时「先查后写」,消除 API 幻觉 | +| `nutui-react-taro-v3-to-v4` | 将 `@nutui/nutui-react-taro` 从 v3 系统升级到 v4 | + +## nutui-react-taro — 先查后写 + +指导 Agent 在编写 NutUI-React Taro 组件代码前,先通过 CLI 或 MCP 查询真实 API 与 Taro 示例,而不是沿用 H5 API 或凭记忆猜测。 + +**它约束 Agent 的行为:** + +- 写组件前先用 `nutui-react-taro info ` 查询 Props,再用 `demo` 获取 Taro 示例。 +- 用 `doc` 核对完整文档,用 `token` 获取 `var(--nutui-*)` Design Token。 +- 不确定组件名时先用 `list` 确认,并优先使用 `--format json` 结构化输出。 + +**安装:** + +```bash +npx skills add jdf2e/nutui-react --skill nutui-react-taro +``` + +## nutui-react-taro-v3-to-v4 — 大版本升级 + +指导 Agent 使用新增的 `migrate` 与 `diff` 能力,把 `@nutui/nutui-react-taro` 从 v3 升级到 v4。该 Skill 特别关注不会触发编译错误的样式、CSS 类名、Design Token、枚举值和默认值变化。 + +**它编排的迁移流程:** + +- 升级依赖前先征求用户确认,并核对锁文件与安装结果。 +- 用 `nutui-react-taro migrate 3 4 --apply ./src --format json` 扫描项目,建立实际使用组件的迁移清单。 +- 对每个命中的组件结合 `migrate --component` 与 `diff 3 4`,同时核对官方迁移说明和 Props 精确差异。 +- 检查项目中针对 `.nut-*` 类名与 `--nutui-*` 变量的自定义样式覆盖。 +- 在目标小程序、H5 或其他 Taro 端完成构建与视觉、交互验证;编译通过不等于迁移完成。 + +**安装:** + +```bash +npx skills add jdf2e/nutui-react --skill nutui-react-taro-v3-to-v4 +``` + +## Skill 与 CLI / MCP 的关系 + +- **CLI** — Agent 主动运行命令查询知识、扫描项目或比较版本。 +- **MCP** — 把同一份查询、迁移和差异对比能力注册成 IDE 原生工具,在对话中按需自动调用。 +- **Skill** — 定义任务触发条件、执行顺序、修改边界和验证要求。 + +如果你的 IDE 支持 MCP,推荐同时启用 MCP 服务,让 Skill 中的查询和迁移步骤直接调用 IDE 原生工具。 + +## 了解更多 + +- [CLI](/#/zh-CN/ai/cli) +- [MCP Server](/#/zh-CN/ai/mcp) +- [For Agents](/#/zh-CN/ai/for-agents) +- [LLMs.txt](/#/zh-CN/ai/llms)