From ecb2295b93ad385becf0a368e0ed5acd5a320f93 Mon Sep 17 00:00:00 2001 From: Juanfran Date: Tue, 12 Mar 2024 12:44:09 +0100 Subject: [PATCH] feat: test new state parser --- libs/plugins-data-parser/src/index.ts | 1 + .../src/lib/utils/parse-state.ts | 199 ++++++++++++++++++ libs/plugins-runtime/src/index.ts | 6 +- 3 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 libs/plugins-data-parser/src/lib/utils/parse-state.ts diff --git a/libs/plugins-data-parser/src/index.ts b/libs/plugins-data-parser/src/index.ts index ff425a4..7964e99 100644 --- a/libs/plugins-data-parser/src/index.ts +++ b/libs/plugins-data-parser/src/index.ts @@ -1,2 +1,3 @@ export { parse, getSelectedUuids } from './lib/utils'; export * from './lib/models'; +export { getPartialState } from './lib/utils/parse-state'; diff --git a/libs/plugins-data-parser/src/lib/utils/parse-state.ts b/libs/plugins-data-parser/src/lib/utils/parse-state.ts new file mode 100644 index 0000000..75bf93d --- /dev/null +++ b/libs/plugins-data-parser/src/lib/utils/parse-state.ts @@ -0,0 +1,199 @@ +import { toCamelCase } from './object.util'; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type UknowCljs = any; + +declare global { + const cljs: UknowCljs; + const app: UknowCljs; +} + +const PATH_SEPARATOR = '/' as const; + +function getPath(path: string): UknowCljs[] { + if (path === 'root') { + return []; + } + return path + .slice(5) + .split(PATH_SEPARATOR) + .map((it) => { + if (it.startsWith('#')) { + return cljs.core.uuid(it.slice(1)); + } else if (it.startsWith(':')) { + return cljs.core.keyword(it.slice(1)); + } else if (it.startsWith('!')) { + return parseInt(it.slice(1), 10); + } else { + return it; + } + }); +} + +const expandableTable = { + 'app:datetime': false, + 'cljs:uuid': false, + 'cljs:keyword': false, + 'cljs:symbol': false, + 'js:date': false, + 'js:boolean': false, + 'js:function': false, + 'js:number': false, + 'js:string': false, + 'js:undefined': false, + 'js:null': false, + 'unkwnown:unknown': false, + 'cljs:vector': true, + 'cljs:list': true, + 'cljs:map': true, + 'cljs:set': true, + 'js:array': true, + 'js:object': true, +} as const; + +function isCljsArray(key: string) { + const valid = ['cljs:vector', 'cljs:list', 'cljs:set']; + + return valid.includes(key); +} + +const valueTable = { + 'app:datetime': (value: unknown) => cljs.core.str(value), + 'cljs:uuid': (value: unknown) => cljs.core.name(value), + 'cljs:keyword': (value: unknown) => cljs.core.name(value), + 'js:date': (value: unknown) => (value as Date).toISOString(), +}; + +function isTypeExpandable(type: keyof typeof expandableTable) { + return expandableTable[type]; +} + +function getType(value: unknown) { + const typeTable = { + 'app:datetime': app.util.time.datetime_QMARK_, + 'cljs:uuid': cljs.core.uuid_QMARK_, + 'cljs:keyword': cljs.core.keyword_QMARK_, + 'cljs:vector': cljs.core.vector_QMARK_, + 'cljs:list': cljs.core.list_QMARK_, + 'cljs:map': cljs.core.map_QMARK_, + 'cljs:set': cljs.core.set_QMARK_, + 'cljs:symbol': cljs.core.symbol_QMARK_, + 'js:date': (value: unknown) => value instanceof Date, + 'js:array': (value: unknown) => Array.isArray(value), + 'js:boolean': (value: unknown) => typeof value === 'boolean', + 'js:function': (value: unknown) => typeof value === 'function', + 'js:number': (value: unknown) => typeof value === 'number', + 'js:string': (value: unknown) => typeof value === 'string', + 'js:undefined': (value: unknown) => typeof value === 'undefined', + 'js:null': (value: unknown) => value === null, + 'js:object': (value: unknown) => + typeof value === 'object' && + value !== null && + value.toString() === '[object Object]', + }; + + for (const [type, fn] of Object.entries(typeTable)) { + if (fn(value)) return type as keyof typeof typeTable; + } + + return 'unknown:unknown' as keyof typeof typeTable; +} + +function getKeyName(key: string | number) { + if (cljs.core.uuid_QMARK_(key)) { + return '#' + cljs.core.name(key); + } else if (cljs.core.keyword_QMARK_(key)) { + return ':' + cljs.core.name(key); + } else if (typeof key === 'string') { + return key; + } else if (typeof key === 'number') { + return `!${key}`; + } + + return key; +} + +function getValue(value: unknown, type: string): unknown { + return type in valueTable + ? valueTable[type as keyof typeof valueTable](value) + : value; +} + +export function getPartialState(path: string, state: UknowCljs) { + // const state = cljs.core.deref(app.main.store.state); + const statePath = getPath(path); + const data = cljs.core.get_in(state, statePath); + const type = getType(data); + const isArray = isCljsArray(type); + const isExpandable = isTypeExpandable(type); + + const map: Record = {}; + const list: unknown[] = []; + + function addEntry( + childPath: string, + propName: string | number, + repValue: unknown, + isExpandable: boolean + ) { + if (isArray) { + list.push(isExpandable ? getPartialState(childPath, state) : repValue); + } else { + if (isExpandable) { + Object.defineProperty(map, propName, { + get() { + return getPartialState(childPath, state); + }, + configurable: true, + enumerable: true, + }); + } else { + map[propName] = repValue; + } + } + } + + if (isExpandable) { + if (type.startsWith('cljs:')) { + const cljsData = cljs.core.clj__GT_js(cljs.core.to_array(data)); + + cljsData.forEach((entry: unknown, index: number) => { + if (cljs.core.map_entry_QMARK_(entry)) { + const key = cljs.core.first(entry); + const name = getKeyName(key); + const value = cljs.core.nth(entry, 1); + const valueType = getType(value); + const repValue = getValue(value, valueType); + const isExpandable = isTypeExpandable(valueType); + const childPath = path + PATH_SEPARATOR + name; + const propName = name.startsWith(':') + ? toCamelCase(name.slice(1)) + : name; + + addEntry(childPath, propName, repValue, isExpandable); + + return; + } else { + const valueType = getType(entry); + const repValue = getValue(entry, valueType); + const isExpandable = isTypeExpandable(valueType); + const childPath = path + PATH_SEPARATOR + getKeyName(index); + + addEntry(childPath, index, repValue, isExpandable); + + return; + } + }); + } else if (type.startsWith('js:')) { + Object.entries(data).forEach(([key, value]) => { + map[key] = value; + }); + } + + return isArray ? list : map; + } + + const repValue = getValue(data, type); + + return repValue; +} diff --git a/libs/plugins-runtime/src/index.ts b/libs/plugins-runtime/src/index.ts index 5c6c2dc..ca7df6d 100644 --- a/libs/plugins-runtime/src/index.ts +++ b/libs/plugins-runtime/src/index.ts @@ -3,7 +3,7 @@ import './lib/plugin-modal'; import { ɵloadPlugin } from './lib/load-plugin'; import { setFileState, setPageState, setSelection, setTheme } from './lib/api'; -import { getSelectedUuids } from 'plugins-parser'; +import { getPartialState, getSelectedUuids } from 'plugins-parser'; repairIntrinsics({ evalTaming: 'unsafeEval', @@ -18,6 +18,10 @@ export function initialize(api: any) { console.log(api); /* eslint-disable */ + (globalThis as any).getPartialState = (path: string) => { + return getPartialState(path, api.getState()); + }; + api.addListener('plugin-page', 'page', (page: any) => { console.log('Page Changed:', page);