import { Scope, VmExtern, type ExpressionSource, type VmValue } from '@cloudpss/expression'; import type { ResourceRevisionVersion } from '../../revision.js'; import type { ResourceContextVersion } from '../../context.js'; import type { Application, Asset, AssetKey, ElementStencilId, FunctionAsset, ModelAsset, Scene, } from '../../application/index.js'; import { V5Migrator } from '../v5.js'; import type { ArgumentValue } from '@cloudpss/expression/definitions'; const elRename = new Map([['chart-tri-surf', 'chart-mesh']]); /** 升级算例 */ class Migrator extends V5Migrator { constructor(readonly app: Application) { super(); } readonly revision = this.app.revision; /** 上下文 */ assetContext(asset: Asset): Scope { return new Scope((key: string) => { if (key === '$$') return this.app as Application & VmValue; if (key === '$') return asset as Asset & VmValue; const { args } = asset as FunctionAsset | ModelAsset; if (!args) return undefined; if (key in args) { return args[key]; } return undefined; }); } /** 上下文 */ sceneContext(scene: Scene): Scope { const isObj = (o: { id: string; key?: string }, key: string) => { if (o.key) return o.key === key; return o.id === key; }; return new Scope((key) => { if (key.startsWith('$')) { const assetKey = key.slice(1) as AssetKey; const asset = this.revision.assets.find((a) => a.key === assetKey); if (!asset) return undefined; if (asset.type === 'static') { return new VmExtern({ value: asset.value, }); } return new VmExtern({ args: asset.args, value: {}, status: 'waiting', running: false, progress: 0, start: () => void 0, send: () => void 0, local: () => void 0, abort: () => void 0, terminate: () => void 0, emtp: { recording: false, startRecording: () => void 0, stopRecording: () => void 0, }, }); } if (isObj(scene, key)) { const variables = scene.variables ?? []; if (!variables.length) return {}; return Object.fromEntries(variables.map((v) => [v.key, v.value])); } for (const cellId in scene.cells) { const cell = scene.cells[cellId]; if (!cell) continue; if (!isObj(cell, key)) continue; return cell.properties ?? {}; } return undefined; }); } /** * 升级算例 */ migrate(): void { if (this.app.context.version < 5) { // App 上下文不需要迁移 this.app.context.version = 5 as ResourceContextVersion; } if (this.revision.version < 5) { for (const asset of this.revision.assets) { switch (asset.type) { case 'function': case 'model': { const context = this.assetContext(asset); this.migrateConfig(asset, context); break; } case 'static': break; } } for (const scene of this.revision.scenes) { const context = this.sceneContext(scene); this.migrateVariables(scene.variables, context); for (const cellId in scene.cells) { const cell = scene.cells[cellId]; if (cell == null) continue; if (cell.shape !== 'element') continue; const newName = elRename.get(cell.definition); if (newName) cell.definition = newName as ElementStencilId; delete (cell as { tag?: unknown }).tag; for (const key in cell.properties) { this.migrateExpression(key, cell.properties, context); } for (const key in cell.events) { this.migrateExpressionSource(key, cell.events, context); } } } this.revision.version = 5 as ResourceRevisionVersion; } } } /** * 升级算例 */ export function migrate(app: Application): void { // 属性从 data 中拆分 for (const scene of app.revision.scenes) { for (const cellId in scene.cells) { const cell = scene.cells[cellId]; if (cell == null || (cell.shape && cell.shape !== 'element')) continue; cell.shape ??= 'element'; const { data } = cell as unknown as { data?: Record }; if (data == null) continue; delete (cell as unknown as { data?: Record }).data; (cell as { scale?: number }).scale ??= Number(data['$scale'] ?? 1) || 1; (cell as { align?: unknown }).align ??= data['$align']; const properties = (cell.properties ??= {}); const events = (cell.events ??= {}); for (const key in data) { const value = data[key]; if (value == null) continue; if (key.startsWith('@')) { events[key.slice(1)] = String(value) as ExpressionSource; } else if (key.startsWith('$')) { continue; } else { properties[key] = value as ArgumentValue; } } } } const m = new Migrator(app); m.migrate(); }