Files
CherryHQ-cherry-studio/v2-refactor-temp/tools/data-classify/scripts/generate-migration.js
fullex c514dcc049 refactor(shared): move packages/shared to src/shared
packages/shared was never a real pnpm workspace package (no package.json); it was referenced only through the @shared TypeScript path alias. Relocate it under src/ via git mv (143 files, detected as pure renames).

Repoint the @shared alias and include globs to src/shared across electron.vite.config.ts, tsconfig.{json,node,web}.json and vitest.config.ts; update scripts/check-custom-exts.ts, scripts/update-languages.ts, the eslint.config.mjs generated-file globs, the data-classify generator output targets, .github/CODEOWNERS path rules, and CLAUDE.md/docs/source-comment references.

The @shared alias name is unchanged, so all 1403 @shared/* import sites resolve without modification. Verified with typecheck:node, typecheck:web and the full test suite (700 files, 9739 tests passing).
2026-05-28 21:02:49 -07:00

388 lines
14 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.extractCategoryData(classification, 'preferences')
// 提取bootConfig相关数据
const bootConfigData = this.extractCategoryData(classification, 'bootConfig')
// 创建目标目录
this.ensureTargetDirectory()
// 生成映射关系文件
this.generateMappings(preferencesData)
this.generateBootConfigMappings(bootConfigData)
console.log('映射关系生成完成!')
this.printSummary(preferencesData)
this.printBootConfigSummary(bootConfigData)
}
loadClassification() {
if (!fs.existsSync(this.classificationFile)) {
throw new Error(`分类文件不存在: ${this.classificationFile}`)
}
const content = fs.readFileSync(this.classificationFile, 'utf8')
return JSON.parse(content)
}
extractCategoryData(classification, targetCategory) {
const allData = []
const sources = ['electronStore', 'redux', 'localStorage', 'dexieSettings']
// 递归提取项目包括children (保持现有逻辑)
const extractItems = (items, source, category, parentKey = '', parentItem = null) => {
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}.`, item)
return
}
// Array-backed preferences need complex mappings; skip them here so
// the generator does not emit conflicting simple mappings.
if (parentItem?.type === 'array') {
return
}
// 处理普通项目
if (item.category === targetCategory && item.status === 'classified' && item.targetKey) {
allData.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(`提取到 ${allData.length}${targetCategory}包含children`)
// 处理重复的targetKey优先使用redux数据
const targetKeyGroups = {}
allData.forEach((item) => {
if (!targetKeyGroups[item.targetKey]) {
targetKeyGroups[item.targetKey] = []
}
targetKeyGroups[item.targetKey].push(item)
})
// 去重按redux > dexieSettings > localStorage > electronStore优先级选择
const sourcePriority = { redux: 4, dexieSettings: 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}${targetCategory}`)
// 按数据源分组
const groupedData = {
electronStore: [],
redux: [],
localStorage: [],
dexieSettings: [],
all: deduplicatedData
}
deduplicatedData.forEach((item) => {
if (groupedData[item.source]) {
groupedData[item.source].push(item)
}
})
return groupedData
}
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
})
})
// 生成localStorage映射 - 简单KV结构
const localStorageMappings = preferencesData.localStorage.map((item) => ({
originalKey: item.originalKey,
targetKey: item.targetKey
}))
// 生成DexieSettings映射 - 简单KV结构
const dexieSettingsMappings = preferencesData.dexieSettings.map((item) => ({
originalKey: item.originalKey,
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 src/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
/**
* Dexie Settings映射关系 - 简单KV结构
*
* Maps Dexie IndexedDB \`settings\` table keys (id field) to new preference target keys.
* The settings table uses a simple KV structure: { id: string, value: any }.
*
* These are simple 1:1 mappings where the value can be used as-is.
* For complex transformations (value conversion, multi-key merging, etc.),
* use ComplexPreferenceMappings with source: 'dexie-settings' instead.
*/
export const DEXIE_SETTINGS_MAPPINGS: ReadonlyArray<{ originalKey: string; targetKey: string }> = ${JSON.stringify(dexieSettingsMappings, null, 2)} as const
/**
* localStorage映射关系 - 简单KV结构
*
* Maps browser localStorage keys to new preference target keys.
* localStorage stores various UI state and provider tokens.
*
* These are simple 1:1 mappings where the value can be used as-is.
* For complex transformations (pattern-based keys, value conversion),
* use ComplexPreferenceMappings with source: 'localStorage' instead.
*/
export const LOCALSTORAGE_MAPPINGS: ReadonlyArray<{ originalKey: string; targetKey: string }> = ${JSON.stringify(localStorageMappings, null, 2)} as const
// === AUTO-GENERATED CONTENT END ===
/**
* 映射统计:
* - ElectronStore项: ${electronStoreMappings.length}
* - Redux Store项: ${preferencesData.redux.length}
* - Redux分类: ${Object.keys(reduxMappings).join(', ')}
* - DexieSettings项: ${dexieSettingsMappings.length}
* - localStorage项: ${localStorageMappings.length}
* - 总配置项: ${preferencesData.all.length}
*
* 使用说明:
* 1. ElectronStore读取: configManager.get(mapping.originalKey)
* 2. Redux读取: 需要解析嵌套路径 reduxData[category][originalKey路径]
* 3. DexieSettings读取: ctx.sources.dexieSettings.get(mapping.originalKey)
* 4. 默认值: 从defaultPreferences.default[mapping.targetKey]获取
*/`
// 写入 PreferencesMappings.ts
const targetFile = path.join(this.targetDir, 'PreferencesMappings.ts')
fs.writeFileSync(targetFile, content, 'utf8')
console.log(`映射关系文件已生成: ${targetFile}`)
}
generateBootConfigMappings(bootConfigData) {
// 生成ElectronStore映射 - 简单结构不需要sourceCategory
const electronStoreMappings = bootConfigData.electronStore.map((item) => ({
originalKey: item.originalKey,
targetKey: item.targetKey
}))
// 生成Redux映射 - 按category分组
const reduxMappings = {}
bootConfigData.redux.forEach((item) => {
if (!reduxMappings[item.sourceCategory]) {
reduxMappings[item.sourceCategory] = []
}
reduxMappings[item.sourceCategory].push({
originalKey: item.originalKey,
targetKey: item.targetKey
})
})
// 生成localStorage映射 - 简单KV结构
const localStorageMappings = bootConfigData.localStorage.map((item) => ({
originalKey: item.originalKey,
targetKey: item.targetKey
}))
// 生成DexieSettings映射 - 简单KV结构
const dexieSettingsMappings = bootConfigData.dexieSettings.map((item) => ({
originalKey: item.originalKey,
targetKey: item.targetKey
}))
// 生成映射关系文件内容
const content = `/**
* Auto-generated boot config mappings from classification.json
* Generated at: ${new Date().toISOString()}
*
* This file contains pure mapping relationships without default values.
* Default values are managed in src/shared/data/bootConfig/bootConfigSchemas.ts
*
* === AUTO-GENERATED CONTENT START ===
*/
import type { BootConfigKey } from '@shared/data/bootConfig/bootConfigTypes'
/**
* ElectronStore映射关系 - 简单一层结构
*
* ElectronStore没有嵌套originalKey直接对应configManager.get(key)
*/
export const BOOT_CONFIG_ELECTRON_STORE_MAPPINGS: ReadonlyArray<{ originalKey: string; targetKey: BootConfigKey }> = ${JSON.stringify(electronStoreMappings, null, 2)} as const
/**
* Redux Store映射关系 - 按category分组支持嵌套路径
*
* Redux Store可能有children结构originalKey可能包含嵌套路径
*/
export const BOOT_CONFIG_REDUX_MAPPINGS = ${JSON.stringify(reduxMappings, null, 2)} as const
/**
* Dexie Settings映射关系 - 简单KV结构
*/
export const BOOT_CONFIG_DEXIE_SETTINGS_MAPPINGS: ReadonlyArray<{ originalKey: string; targetKey: BootConfigKey }> = ${JSON.stringify(dexieSettingsMappings, null, 2)} as const
/**
* localStorage映射关系 - 简单KV结构
*/
export const BOOT_CONFIG_LOCALSTORAGE_MAPPINGS: ReadonlyArray<{ originalKey: string; targetKey: BootConfigKey }> = ${JSON.stringify(localStorageMappings, null, 2)} as const
// === AUTO-GENERATED CONTENT END ===
/**
* 映射统计:
* - ElectronStore项: ${electronStoreMappings.length}
* - Redux Store项: ${bootConfigData.redux.length}
* - Redux分类: ${Object.keys(reduxMappings).join(', ') || 'none'}
* - DexieSettings项: ${dexieSettingsMappings.length}
* - localStorage项: ${localStorageMappings.length}
* - 总配置项: ${bootConfigData.all.length}
*/`
// 写入 BootConfigMappings.ts
const targetFile = path.join(this.targetDir, 'BootConfigMappings.ts')
fs.writeFileSync(targetFile, content, 'utf8')
console.log(`Boot config映射关系文件已生成: ${targetFile}`)
}
printSummary(preferencesData) {
console.log(`\n生成摘要 (Preferences):`)
console.log(`- 输出文件: PreferencesMappings.ts`)
console.log(`- ElectronStore映射: ${preferencesData.electronStore.length}`)
console.log(`- Redux Store映射: ${preferencesData.redux.length}`)
console.log(`- DexieSettings映射: ${preferencesData.dexieSettings.length}`)
console.log(`- localStorage映射: ${preferencesData.localStorage.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}`))
}
}
printBootConfigSummary(bootConfigData) {
console.log(`\n生成摘要 (BootConfig):`)
console.log(`- 输出文件: BootConfigMappings.ts`)
console.log(`- ElectronStore映射: ${bootConfigData.electronStore.length}`)
console.log(`- Redux Store映射: ${bootConfigData.redux.length}`)
console.log(`- DexieSettings映射: ${bootConfigData.dexieSettings.length}`)
console.log(`- localStorage映射: ${bootConfigData.localStorage.length}`)
console.log(`- 总配置项: ${bootConfigData.all.length}`)
}
}
// 主执行逻辑
if (require.main === module) {
try {
const generator = new SimpleMappingGenerator()
generator.generate()
} catch (error) {
console.error('生成失败:', error.message)
process.exit(1)
}
}
module.exports = SimpleMappingGenerator