#!/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