import { get, isArray, isEmpty, isString, set, uniq } from 'lodash'; import type { PageDSL4and5, PageDSL6 } from '../../types/index.js'; const WIDGET_TO_TRIGGER = { INPUT_WIDGET: ['onSubmit', 'onTextChanged'], TABLE_WIDGET: ['onRowClicked', 'onRowSelected', 'onPageChange', 'onFiltersChanged', 'onSearchTextChanged'], GRID_WIDGET: ['onCellClicked'], CHART_WIDGET: ['onSelectData'], RICH_TEXT_EDITOR_WIDGET: ['onSelectData'], DROP_DOWN_WIDGET: ['onOptionChange'], BUTTON_WIDGET: ['onClick'], FORM_BUTTON_WIDGET: ['onClick'], IMAGE_WIDGET: ['onClick'], DATE_PICKER_WIDGET: ['onDateSelected'], VIDEO_WIDGET: ['onPlay', 'onPause', 'onEnd'], RADIO_GROUP_WIDGET: ['onSelectionChange'], CHECKBOX_WIDGET: ['onSelectionChange'], TABS_WIDGET: ['onTabSelected'], CODE_WIDGET: ['onChange'], IFRAME_WIDGET: ['onMessageReceived'], MAP_WIDGET: ['onMarkerClick'], SLIDEOUT_WIDGET: ['onOpen', 'onClose'], MODAL_WIDGET: ['onOpen', 'onClose'], ICON_WIDGET: ['onClick'] }; // This migration code uses strings instead of variables like CANVAS_WIDGET for two reasons: // 1. These strings will never change, even if we change the types in the future // 2. We do want to make these types much stricter in the future, but for now export const migrate5to6 = (widget: PageDSL4and5): PageDSL6 => { if (isArray(widget.dynamicTriggerPathList)) { const bindingKeysToRemove: string[] = []; // This is a special case we've found where the dynamicTriggerPathList was entirely missing from some properties WIDGET_TO_TRIGGER[widget.type]?.forEach((path) => { if ( !isEmpty(widget[path]) && isArray(widget.dynamicTriggerPathList) && !widget.dynamicTriggerPathList.some(({ key }) => key === path) ) { widget.dynamicTriggerPathList.push({ key: path }); } }); widget.dynamicTriggerPathList.forEach(({ key }) => { // Some apps have duplicate trigger keys, these will get cleaned up later if (bindingKeysToRemove.includes(key)) return; const value = get(widget, key); bindingKeysToRemove.push(key); if (isArray(value)) { const newEvents = value.map(extractActionFromString); set(widget, key, newEvents); } else if (isString(value)) { const newEvent = extractActionFromString(value, 0); set(widget, key, [newEvent]); } }); // Remove duplicates widget.dynamicTriggerPathList = uniq(widget.dynamicTriggerPathList.map(({ key }) => key)).map((key: string) => ({ key })); widget.dynamicBindingPathList = ((widget.dynamicBindingPathList as { key: string }[]) ?? []).filter(({ key }) => { return !bindingKeysToRemove.includes(key); }); widget.dynamicPropertyPathList = ((widget.dynamicPropertyPathList as { key: string }[]) ?? []).filter(({ key }) => { return !bindingKeysToRemove.includes(key); }); } widget.children?.forEach((child: PageDSL4and5) => { if (!child) return; migrate5to6(child); }); return widget as unknown as PageDSL6; }; type ActionDef = | { id: string; type: 'executeApi'; apiNames: string[]; } | { id: string; type: 'runJs'; code: string; } | { id: string; type: 'navigateTo'; url: string; newWindow: boolean; arguments: string; } | { id: string; type: 'controlSlideout'; name: string; direction: 'open' | 'close'; }; const strictlyMatchApiRun = /^\s*\{\{\s*(\w+)\.run\(\)\s*\}\}\s*$/; const strictlyMatchOpenClose = /^\s*\{\{\s*(\w+)\.(open|close)\(\)\s*\}\}\s*$/; const DATA_BIND_REGEX_GLOBAL = /{{([\s\S]*?)}}/g; function extractActionFromString(value: unknown, index: number): ActionDef { if (!isString(value)) { throw new Error(`Value is not string: ${JSON.stringify(value)}`); } const matchesApiRun = value.match(strictlyMatchApiRun); if (matchesApiRun?.[1]) { return { id: `id${index}`, type: 'executeApi', apiNames: [matchesApiRun?.[1]] }; } const matchesSlideoutOpenClose = value.match(strictlyMatchOpenClose); if (matchesSlideoutOpenClose?.[1]) { const slideoutName = matchesSlideoutOpenClose?.[1]; return { id: `id${index}`, type: 'controlSlideout', name: slideoutName, direction: matchesSlideoutOpenClose[2] as 'open' | 'close' }; } const codeMatches = [...value.matchAll(DATA_BIND_REGEX_GLOBAL)]; return { id: `id${index}`, type: 'runJs', code: codeMatches.map((matches) => matches.slice(1).join('')).join('\n') }; }