import { type JsonValue, fromCanonicalJson, CanonicalPrimitive } from './content-canonical' export function buildContent( locale: string | undefined, locales: string[], variant: string | undefined, variants: string[], content: JsonValue ): [JsonValue, boolean] { function check(all: string[], specified: string[], query: string): boolean { const related = specified.filter((t) => all.includes(t)) all = related.length ? related : all return all.includes(query) } function predicate(tags?: string[]) { if (!tags || !tags.length) { return true } if (locale && !check(locales, tags, locale)) { return false } return !variant || check(variants, tags, variant) } const options = { predicate: predicate, expand: false } const canonical = fromCanonicalJson(content) const reduced = canonical.reduce(options) return [reduced, options.expand] } export function expandContent(content: JsonValue, sources: JsonValue[]): JsonValue { function resolve(key: string): JsonValue { for (const source of sources) { const value = getPath(source, key) if (value !== undefined) { return value } } return `$\{${key}\}` } function traverse(item: JsonValue): JsonValue { if (item !== null) { if (item instanceof CanonicalPrimitive) { return item.resolve(resolve) } else if (Array.isArray(item)) { return item.map((i) => traverse(i)) } else if (typeof item === 'object') { const record: Record = {} for (const [k, v] of Object.entries(item)) { record[k] = traverse(v) } return record } } return item } return traverse(content) } // function getPath(obj: any, path: string, sep = '.'): any { // TODO REFACTOR THIS // const keys = path.split(sep) // let cur = obj // // for (const key of keys) { // if (cur == null || typeof cur !== 'object') return undefined // cur = cur[key] // } // // return cur // } function toPath(path: string): Array { // Matches: // - bare tokens: foo, bar // - [123] numeric indices // - ["str"] or ['str'] quoted keys const re = /[^.[\]]+|\[(?:([0-9]+)|(["'])(.*?)\2)\]/g const tokens: Array = [] let m: RegExpExecArray | null while ((m = re.exec(path))) { if (m[1] !== undefined) { tokens.push(Number(m[1])) // [123] } else if (m[3] !== undefined) { tokens.push(m[3]) // ["key"] / ['key'] } else { tokens.push(m[0]) // bare token } } return tokens } function getPath(obj: unknown, path: string): JsonValue | undefined { const steps = toPath(path) let cur: any = obj for (const step of steps) { if (cur == null) return undefined if (typeof step === 'number') { if (!Array.isArray(cur) || step < 0 || step >= cur.length) return undefined cur = cur[step] } else { cur = cur[step] } } return cur as JsonValue }