script/lib/variable-collection.ts (85 lines of code) (raw):
import flatten from 'flat'
import isString from 'lodash/isString'
import kebabCase from 'lodash/kebabCase'
import set from 'lodash/set'
import {resolveValue} from '../../src/utils'
type PathItem = string | number
export interface ModeVariable {
name: string
path: PathItem[]
value: any
}
export function getFullName(prefix: string, path: PathItem[]) {
return [prefix, ...path].map(value => (isString(value) ? kebabCase(value) : value)).join('-')
}
export default class VariableCollection {
public readonly name: string
public readonly prefix: string
public readonly parent: string | null
private data: Map<string, ModeVariable> = new Map()
constructor(name: string, prefix: string, parent: string | null) {
this.name = name
this.prefix = prefix
this.parent = parent
}
public addFromObject(data: Record<string, unknown>) {
const flattened: Record<string, unknown> = flatten(data)
for (const [key, value] of Object.entries(flattened)) {
const path = key.split('.')
this.add(path, value)
}
}
public add(path: PathItem[], value: any) {
if (Array.isArray(value)) {
for (const idx in value) {
const newPath = [...path, idx]
this.add(newPath, value[idx])
}
return
}
const fullName = getFullName(this.prefix, path)
if (value === 'transparent') {
value = 'rgba(0,0,0,0)'
}
const variable: ModeVariable = {name: fullName, path, value}
this.data.set(fullName, variable)
}
public merge(other: VariableCollection) {
for (const modeVar of other.flattened()) {
if (!this.data.has(modeVar.name)) {
this.add(modeVar.path, modeVar.value)
}
}
}
public flattened(): ReadonlyArray<ModeVariable> {
const tree = this.unresolvedTree()
return [...this.data.values()].map(variable => {
return {
...variable,
value: resolveValue(variable.value, tree)
}
})
}
public getByName(fullName: string): ModeVariable | undefined {
return this.data.get(fullName)
}
public tree(): Readonly<Record<string, any>> {
let output = {} as Record<string, any>
const tree = this.unresolvedTree()
for (const variable of this.data.values()) {
const value = resolveValue(variable.value, tree)
set(output, variable.path, value)
}
return output
}
public [Symbol.iterator]() {
return this.flattened()[Symbol.iterator]()
}
private unresolvedTree(): Readonly<Record<string, any>> {
let output = {} as Record<string, any>
for (const variable of this.data.values()) {
set(output, variable.path, variable.value)
}
return output
}
}