import { renderFn, get, Scope, ResolveFn } from 'micromustache'; import type { JsonValue } from '@directus/types'; import { parseJSON } from './parse-json.js'; type Mustache = T extends string ? JsonValue : T extends Array ? Array> : T extends Record ? { [K in keyof T]: Mustache } : T; export function applyOptionsData( options: Record, data: Record, skipUndefinedKeys: string[] = [] ): Record { return Object.fromEntries( Object.entries(options).map(([key, value]) => [key, renderMustache(value, data, skipUndefinedKeys.includes(key))]) ); } function resolveFn(skipUndefined: boolean): (path: string, scope: Scope) => any { return (path, scope) => { const value = get(scope, path); if (value !== undefined || !skipUndefined) { return typeof value === 'object' ? JSON.stringify(value) : value; } else { return `{{ ${path} }}`; } }; } function renderMustache(item: T, scope: Scope, skipUndefined: boolean): Mustache { if (typeof item === 'string') { const raw = item.match(/^\{\{\s*([^}\s]+)\s*\}\}$/); if (raw !== null) { const value = get(scope, raw[1]!); if (value !== undefined) { return value; } } return renderFn(item, resolveFn(skipUndefined) as ResolveFn, scope, { explicit: true }) as Mustache; } else if (Array.isArray(item)) { return item.map((element) => renderMustache(element, scope, skipUndefined)) as Mustache; } else if (typeof item === 'object' && item !== null) { return Object.fromEntries( Object.entries(item).map(([key, value]) => [key, renderMustache(value, scope, skipUndefined)]) ) as Mustache; } else { return item as Mustache; } } export function optionToObject(option: T): Exclude { return typeof option === 'string' ? parseJSON(option) : option; } export function optionToString(option: unknown): string { return typeof option === 'object' ? JSON.stringify(option) : String(option); }