Some checks failed
Build and deploy updated apps / Build & deploy (push) Failing after 1m7s
122 lines
3.0 KiB
TypeScript
122 lines
3.0 KiB
TypeScript
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<string, JsonValue> = {}
|
|
|
|
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<string | number> {
|
|
// Matches:
|
|
// - bare tokens: foo, bar
|
|
// - [123] numeric indices
|
|
// - ["str"] or ['str'] quoted keys
|
|
const re = /[^.[\]]+|\[(?:([0-9]+)|(["'])(.*?)\2)\]/g;
|
|
const tokens: Array<string | number> = [];
|
|
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;
|
|
}
|