cherry-studio/v2-refactor-temp/tools/data-classify/scripts/generate-migration.js
fullex 806a294508 feat: add v2-refactor-temp directory for V2 refactoring tools
- Add data-classify tools for data inventory extraction and code generation
  - Include consolidated Chinese documentation (README.md)
  - Update generated file path references

  This temporary directory will be removed after V2 refactor is complete.
2025-11-29 11:55:45 +08:00

240 lines
7.8 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
const fs = require('fs')
const path = require('path')
class SimpleMappingGenerator {
constructor() {
this.dataDir = path.resolve(__dirname, '../data')
this.targetDir = path.resolve(__dirname, '../../../../src/main/data/migration/v2/migrators/mappings')
this.classificationFile = path.join(this.dataDir, 'classification.json')
}
generate() {
console.log('开始生成简化的映射关系代码...')
// 读取分类数据
const classification = this.loadClassification()
// 提取preferences相关数据
const preferencesData = this.extractPreferencesData(classification)
// 创建目标目录
this.ensureTargetDirectory()
// 生成映射关系文件
this.generateMappings(preferencesData)
console.log('映射关系生成完成!')
this.printSummary(preferencesData)
}
loadClassification() {
if (!fs.existsSync(this.classificationFile)) {
throw new Error(`分类文件不存在: ${this.classificationFile}`)
}
const content = fs.readFileSync(this.classificationFile, 'utf8')
return JSON.parse(content)
}
extractPreferencesData(classification) {
const allPreferencesData = []
const sources = ['electronStore', 'redux', 'localStorage']
// 递归提取项目包括children (保持现有逻辑)
const extractItems = (items, source, category, parentKey = '') => {
if (!Array.isArray(items)) return
items.forEach((item) => {
// 处理有children的项目
if (item.children && Array.isArray(item.children)) {
console.log(`处理children项: ${source}/${category}/${item.originalKey}`)
extractItems(item.children, source, category, `${parentKey}${item.originalKey}.`)
return
}
// 处理普通项目
if (item.category === 'preferences' && item.status === 'classified' && item.targetKey) {
allPreferencesData.push({
...item,
source,
sourceCategory: category,
originalKey: parentKey + item.originalKey, // 包含父级路径
fullPath: `${source}/${category}/${parentKey}${item.originalKey}`
})
}
})
}
sources.forEach((source) => {
if (classification.classifications[source]) {
Object.keys(classification.classifications[source]).forEach((category) => {
const items = classification.classifications[source][category]
extractItems(items, source, category)
})
}
})
console.log(`提取到 ${allPreferencesData.length} 个preferences项包含children`)
// 处理重复的targetKey优先使用redux数据
const targetKeyGroups = {}
allPreferencesData.forEach((item) => {
if (!targetKeyGroups[item.targetKey]) {
targetKeyGroups[item.targetKey] = []
}
targetKeyGroups[item.targetKey].push(item)
})
// 去重按redux > localStorage > electronStore优先级选择
const sourcePriority = { redux: 3, localStorage: 2, electronStore: 1 }
const deduplicatedData = []
Object.keys(targetKeyGroups).forEach((targetKey) => {
const items = targetKeyGroups[targetKey]
if (items.length > 1) {
console.log(`发现重复targetKey: ${targetKey},共${items.length}`)
items.forEach((item) => console.log(` - ${item.fullPath}`))
// 按优先级排序,选择最高优先级的项
items.sort((a, b) => sourcePriority[b.source] - sourcePriority[a.source])
const selected = items[0]
console.log(` 选择: ${selected.fullPath}`)
deduplicatedData.push(selected)
} else {
deduplicatedData.push(items[0])
}
})
console.log(`去重后剩余 ${deduplicatedData.length} 个preferences项`)
// 按数据源分组
const preferencesData = {
electronStore: [],
redux: [],
localStorage: [],
all: deduplicatedData
}
deduplicatedData.forEach((item) => {
if (preferencesData[item.source]) {
preferencesData[item.source].push(item)
}
})
return preferencesData
}
ensureTargetDirectory() {
if (!fs.existsSync(this.targetDir)) {
fs.mkdirSync(this.targetDir, { recursive: true })
}
}
generateMappings(preferencesData) {
// 生成ElectronStore映射 - 简单结构不需要sourceCategory
const electronStoreMappings = preferencesData.electronStore.map((item) => ({
originalKey: item.originalKey,
targetKey: item.targetKey
}))
// 生成Redux映射 - 按category分组
const reduxMappings = {}
preferencesData.redux.forEach((item) => {
if (!reduxMappings[item.sourceCategory]) {
reduxMappings[item.sourceCategory] = []
}
reduxMappings[item.sourceCategory].push({
originalKey: item.originalKey, // 可能包含嵌套路径,如"codeEditor.enabled"
targetKey: item.targetKey
})
})
// 生成映射关系文件内容
const content = `/**
* Auto-generated preference mappings from classification.json
* Generated at: ${new Date().toISOString()}
*
* This file contains pure mapping relationships without default values.
* Default values are managed in packages/shared/data/preferences.ts
*
* === AUTO-GENERATED CONTENT START ===
*/
/**
* ElectronStore映射关系 - 简单一层结构
*
* ElectronStore没有嵌套originalKey直接对应configManager.get(key)
*/
export const ELECTRON_STORE_MAPPINGS = ${JSON.stringify(electronStoreMappings, null, 2)} as const
/**
* Redux Store映射关系 - 按category分组支持嵌套路径
*
* Redux Store可能有children结构originalKey可能包含嵌套路径:
* - 直接字段: "theme" -> reduxData.settings.theme
* - 嵌套字段: "codeEditor.enabled" -> reduxData.settings.codeEditor.enabled
* - 多层嵌套: "exportMenuOptions.docx" -> reduxData.settings.exportMenuOptions.docx
*/
export const REDUX_STORE_MAPPINGS = ${JSON.stringify(reduxMappings, null, 2)} as const
// === AUTO-GENERATED CONTENT END ===
/**
* 映射统计:
* - ElectronStore项: ${electronStoreMappings.length}
* - Redux Store项: ${preferencesData.redux.length}
* - Redux分类: ${Object.keys(reduxMappings).join(', ')}
* - 总配置项: ${preferencesData.all.length}
*
* 使用说明:
* 1. ElectronStore读取: configManager.get(mapping.originalKey)
* 2. Redux读取: 需要解析嵌套路径 reduxData[category][originalKey路径]
* 3. 默认值: 从defaultPreferences.default[mapping.targetKey]获取
*/`
// 写入文件
const targetFile = path.join(this.targetDir, 'PreferencesMappings.ts')
fs.writeFileSync(targetFile, content, 'utf8')
console.log(`映射关系文件已生成: ${targetFile}`)
}
printSummary(preferencesData) {
console.log(`\n生成摘要:`)
console.log(`- 输出文件: PreferencesMappings.ts`)
console.log(`- ElectronStore映射: ${preferencesData.electronStore.length}`)
console.log(`- Redux Store映射: ${preferencesData.redux.length}`)
console.log(`- 总配置项: ${preferencesData.all.length}`)
// 显示Redux分类
const reduxCategories = [...new Set(preferencesData.redux.map((item) => item.sourceCategory))]
console.log(`- Redux分类: ${reduxCategories.join(', ')}`)
// 显示一些嵌套路径的例子
const nestedKeys = preferencesData.redux
.filter((item) => item.originalKey.includes('.'))
.slice(0, 5)
.map((item) => item.originalKey)
if (nestedKeys.length > 0) {
console.log(`\n嵌套路径示例:`)
nestedKeys.forEach((key) => console.log(` - ${key}`))
}
}
}
// 主执行逻辑
if (require.main === module) {
try {
const generator = new SimpleMappingGenerator()
generator.generate()
} catch (error) {
console.error('生成失败:', error.message)
process.exit(1)
}
}
module.exports = SimpleMappingGenerator