better error reporting

This commit is contained in:
Henry Jameson 2024-10-25 16:39:37 +03:00
parent f46adb6724
commit b55aeb54f6
2 changed files with 260 additions and 239 deletions

View file

@ -732,10 +732,18 @@ export default {
.reduce((acc, [k, v]) => ({ ...acc, [k]: `color | ${v}` }), {}) .reduce((acc, [k, v]) => ({ ...acc, [k]: `color | ${v}` }), {})
} }
const virtualDirectivesRule = {
component: 'Root',
directives: Object.fromEntries(
virtualDirectives.map(vd => [`--${vd.name}`, `${vd.valType} | ${vd.value}`])
)
}
const rules = init({ const rules = init({
inputRuleset: [ inputRuleset: [
...editorFriendlyToOriginal.value, paletteRule,
paletteRule virtualDirectivesRule,
...editorFriendlyToOriginal.value
], ],
ultimateBackgroundColor: '#000000', ultimateBackgroundColor: '#000000',
liteMode: true, liteMode: true,

View file

@ -63,54 +63,62 @@ export const findShadow = (shadows, { dynamicVars, staticVars }) => {
} }
export const findColor = (color, { dynamicVars, staticVars }) => { export const findColor = (color, { dynamicVars, staticVars }) => {
if (typeof color !== 'string' || (!color.startsWith('--') && !color.startsWith('$'))) return color try {
let targetColor = null if (typeof color !== 'string' || (!color.startsWith('--') && !color.startsWith('$'))) return color
if (color.startsWith('--')) { let targetColor = null
// Modifier support is pretty much for v2 themes only if (color.startsWith('--')) {
const [variable, modifier] = color.split(/,/g).map(str => str.trim()) // Modifier support is pretty much for v2 themes only
const variableSlot = variable.substring(2) const [variable, modifier] = color.split(/,/g).map(str => str.trim())
if (variableSlot === 'stack') { const variableSlot = variable.substring(2)
const { r, g, b } = dynamicVars.stacked if (variableSlot === 'stack') {
targetColor = { r, g, b } const { r, g, b } = dynamicVars.stacked
} else if (variableSlot.startsWith('parent')) {
if (variableSlot === 'parent') {
const { r, g, b } = dynamicVars.lowerLevelBackground
targetColor = { r, g, b } targetColor = { r, g, b }
} else if (variableSlot.startsWith('parent')) {
if (variableSlot === 'parent') {
const { r, g, b } = dynamicVars.lowerLevelBackground
targetColor = { r, g, b }
} else {
const virtualSlot = variableSlot.replace(/^parent/, '')
targetColor = convert(dynamicVars.lowerLevelVirtualDirectivesRaw[virtualSlot]).rgb
}
} else { } else {
const virtualSlot = variableSlot.replace(/^parent/, '') switch (variableSlot) {
targetColor = convert(dynamicVars.lowerLevelVirtualDirectivesRaw[virtualSlot]).rgb case 'inheritedBackground':
targetColor = convert(dynamicVars.inheritedBackground).rgb
break
case 'background':
targetColor = convert(dynamicVars.background).rgb
break
default:
targetColor = convert(staticVars[variableSlot]).rgb
}
} }
} else {
switch (variableSlot) { if (modifier) {
case 'inheritedBackground': const effectiveBackground = dynamicVars.lowerLevelBackground ?? targetColor
targetColor = convert(dynamicVars.inheritedBackground).rgb const isLightOnDark = relativeLuminance(convert(effectiveBackground).rgb) < 0.5
break const mod = isLightOnDark ? 1 : -1
case 'background': targetColor = brightness(Number.parseFloat(modifier) * mod, targetColor).rgb
targetColor = convert(dynamicVars.background).rgb
break
default:
targetColor = convert(staticVars[variableSlot]).rgb
} }
} }
if (modifier) { if (color.startsWith('$')) {
const effectiveBackground = dynamicVars.lowerLevelBackground ?? targetColor try {
const isLightOnDark = relativeLuminance(convert(effectiveBackground).rgb) < 0.5 targetColor = process(color, colorFunctions, { findColor }, { dynamicVars, staticVars })
const mod = isLightOnDark ? 1 : -1 } catch (e) {
targetColor = brightness(Number.parseFloat(modifier) * mod, targetColor).rgb console.error('Failure executing color function', e)
targetColor = '#FF00FF'
}
} }
// Color references other color
return targetColor
} catch (e) {
throw new Error(`Couldn't find color "${color}", variables are:
Static:
${JSON.stringify(staticVars, null, 2)}
Dynamic:
${JSON.stringify(dynamicVars, null, 2)}`)
} }
if (color.startsWith('$')) {
try {
targetColor = process(color, colorFunctions, { findColor }, { dynamicVars, staticVars })
} catch (e) {
console.error('Failure executing color function', e)
targetColor = '#FF00FF'
}
}
// Color references other color
return targetColor
} }
const getTextColorAlpha = (directives, intendedTextColor, dynamicVars, staticVars) => { const getTextColorAlpha = (directives, intendedTextColor, dynamicVars, staticVars) => {
@ -241,212 +249,217 @@ export const init = ({
const nonEditableComponents = new Set(Object.values(components).filter(c => c.notEditable).map(c => c.name)) const nonEditableComponents = new Set(Object.values(components).filter(c => c.notEditable).map(c => c.name))
const processCombination = (combination) => { const processCombination = (combination) => {
const selector = ruleToSelector(combination, true) try {
const cssSelector = ruleToSelector(combination) const selector = ruleToSelector(combination, true)
const cssSelector = ruleToSelector(combination)
const parentSelector = selector.split(/ /g).slice(0, -1).join(' ') const parentSelector = selector.split(/ /g).slice(0, -1).join(' ')
const soloSelector = selector.split(/ /g).slice(-1)[0] const soloSelector = selector.split(/ /g).slice(-1)[0]
const lowerLevelSelector = parentSelector const lowerLevelSelector = parentSelector
let lowerLevelBackground = computed[lowerLevelSelector]?.background let lowerLevelBackground = computed[lowerLevelSelector]?.background
if (editMode && !lowerLevelBackground) { if (editMode && !lowerLevelBackground) {
// FIXME hack for editor until it supports handling component backgrounds // FIXME hack for editor until it supports handling component backgrounds
lowerLevelBackground = '#00FFFF' lowerLevelBackground = '#00FFFF'
} }
const lowerLevelVirtualDirectives = computed[lowerLevelSelector]?.virtualDirectives const lowerLevelVirtualDirectives = computed[lowerLevelSelector]?.virtualDirectives
const lowerLevelVirtualDirectivesRaw = computed[lowerLevelSelector]?.virtualDirectivesRaw const lowerLevelVirtualDirectivesRaw = computed[lowerLevelSelector]?.virtualDirectivesRaw
const dynamicVars = computed[selector] || { const dynamicVars = computed[selector] || {
lowerLevelBackground, lowerLevelBackground,
lowerLevelVirtualDirectives, lowerLevelVirtualDirectives,
lowerLevelVirtualDirectivesRaw lowerLevelVirtualDirectivesRaw
}
// Inheriting all of the applicable rules
const existingRules = ruleset.filter(findRules(combination))
const computedDirectives =
existingRules
.map(r => r.directives)
.reduce((acc, directives) => ({ ...acc, ...directives }), {})
const computedRule = {
...combination,
directives: computedDirectives
}
computed[selector] = computed[selector] || {}
computed[selector].computedRule = computedRule
computed[selector].dynamicVars = dynamicVars
if (virtualComponents.has(combination.component)) {
const virtualName = [
'--',
combination.component.toLowerCase(),
combination.variant === 'normal'
? ''
: combination.variant[0].toUpperCase() + combination.variant.slice(1).toLowerCase(),
...sortBy(combination.state.filter(x => x !== 'normal')).map(state => state[0].toUpperCase() + state.slice(1).toLowerCase())
].join('')
let inheritedTextColor = computedDirectives.textColor
let inheritedTextAuto = computedDirectives.textAuto
let inheritedTextOpacity = computedDirectives.textOpacity
let inheritedTextOpacityMode = computedDirectives.textOpacityMode
const lowerLevelTextSelector = [...selector.split(/ /g).slice(0, -1), soloSelector].join(' ')
const lowerLevelTextRule = computed[lowerLevelTextSelector]
if (inheritedTextColor == null || inheritedTextOpacity == null || inheritedTextOpacityMode == null) {
inheritedTextColor = computedDirectives.textColor ?? lowerLevelTextRule.textColor
inheritedTextAuto = computedDirectives.textAuto ?? lowerLevelTextRule.textAuto
inheritedTextOpacity = computedDirectives.textOpacity ?? lowerLevelTextRule.textOpacity
inheritedTextOpacityMode = computedDirectives.textOpacityMode ?? lowerLevelTextRule.textOpacityMode
} }
const newTextRule = { // Inheriting all of the applicable rules
...computedRule, const existingRules = ruleset.filter(findRules(combination))
directives: { const computedDirectives =
...computedRule.directives, existingRules
textColor: inheritedTextColor, .map(r => r.directives)
textAuto: inheritedTextAuto ?? 'preserve', .reduce((acc, directives) => ({ ...acc, ...directives }), {})
textOpacity: inheritedTextOpacity, const computedRule = {
textOpacityMode: inheritedTextOpacityMode
}
}
dynamicVars.inheritedBackground = lowerLevelBackground
dynamicVars.stacked = convert(stacked[lowerLevelSelector]).rgb
const intendedTextColor = convert(findColor(inheritedTextColor, { dynamicVars, staticVars })).rgb
const textColor = newTextRule.directives.textAuto === 'no-auto'
? intendedTextColor
: getTextColor(
convert(stacked[lowerLevelSelector]).rgb,
intendedTextColor,
newTextRule.directives.textAuto === 'preserve'
)
const virtualDirectives = computed[lowerLevelSelector].virtualDirectives || {}
const virtualDirectivesRaw = computed[lowerLevelSelector].virtualDirectivesRaw || {}
// Storing color data in lower layer to use as custom css properties
virtualDirectives[virtualName] = getTextColorAlpha(newTextRule.directives, textColor, dynamicVars)
virtualDirectivesRaw[virtualName] = textColor
computed[lowerLevelSelector].virtualDirectives = virtualDirectives
computed[lowerLevelSelector].virtualDirectivesRaw = virtualDirectivesRaw
return {
dynamicVars,
selector: cssSelector.split(/ /g).slice(0, -1).join(' '),
...combination,
directives: {},
virtualDirectives: {
[virtualName]: getTextColorAlpha(newTextRule.directives, textColor, dynamicVars)
},
virtualDirectivesRaw: {
[virtualName]: textColor
}
}
} else {
computed[selector] = computed[selector] || {}
// TODO: DEFAULT TEXT COLOR
const lowerLevelStackedBackground = stacked[lowerLevelSelector] || convert(ultimateBackgroundColor).rgb
if (computedDirectives.background) {
let inheritRule = null
const variantRules = ruleset.filter(
findRules({
component: combination.component,
variant: combination.variant,
parent: combination.parent
})
)
const lastVariantRule = variantRules[variantRules.length - 1]
if (lastVariantRule) {
inheritRule = lastVariantRule
} else {
const normalRules = ruleset.filter(findRules({
component: combination.component,
parent: combination.parent
}))
const lastNormalRule = normalRules[normalRules.length - 1]
inheritRule = lastNormalRule
}
const inheritSelector = ruleToSelector({ ...inheritRule, parent: combination.parent }, true)
const inheritedBackground = computed[inheritSelector].background
dynamicVars.inheritedBackground = inheritedBackground
const rgb = convert(findColor(computedDirectives.background, { dynamicVars, staticVars })).rgb
if (!stacked[selector]) {
let blend
const alpha = computedDirectives.opacity ?? 1
if (alpha >= 1) {
blend = rgb
} else if (alpha <= 0) {
blend = lowerLevelStackedBackground
} else {
blend = alphaBlend(rgb, computedDirectives.opacity, lowerLevelStackedBackground)
}
stacked[selector] = blend
computed[selector].background = { ...rgb, a: computedDirectives.opacity ?? 1 }
}
}
if (computedDirectives.shadow) {
dynamicVars.shadow = flattenDeep(findShadow(flattenDeep(computedDirectives.shadow), { dynamicVars, staticVars }))
}
if (!stacked[selector]) {
computedDirectives.background = 'transparent'
computedDirectives.opacity = 0
stacked[selector] = lowerLevelStackedBackground
computed[selector].background = { ...lowerLevelStackedBackground, a: 0 }
}
dynamicVars.stacked = stacked[selector]
dynamicVars.background = computed[selector].background
const dynamicSlots = Object.entries(computedDirectives).filter(([k, v]) => k.startsWith('--'))
dynamicSlots.forEach(([k, v]) => {
const [type, value] = v.split('|').map(x => x.trim()) // woah, Extreme!
switch (type) {
case 'color': {
const color = findColor(value, { dynamicVars, staticVars })
dynamicVars[k] = color
if (combination.component === rootComponentName) {
staticVars[k.substring(2)] = color
}
break
}
case 'shadow': {
const shadow = value.split(/,/g).map(s => s.trim()).filter(x => x)
dynamicVars[k] = shadow
if (combination.component === rootComponentName) {
staticVars[k.substring(2)] = shadow
}
break
}
case 'generic': {
dynamicVars[k] = value
if (combination.component === rootComponentName) {
staticVars[k.substring(2)] = value
}
break
}
}
})
const rule = {
dynamicVars,
selector: cssSelector,
...combination, ...combination,
directives: computedDirectives directives: computedDirectives
} }
return rule computed[selector] = computed[selector] || {}
computed[selector].computedRule = computedRule
computed[selector].dynamicVars = dynamicVars
if (virtualComponents.has(combination.component)) {
const virtualName = [
'--',
combination.component.toLowerCase(),
combination.variant === 'normal'
? ''
: combination.variant[0].toUpperCase() + combination.variant.slice(1).toLowerCase(),
...sortBy(combination.state.filter(x => x !== 'normal')).map(state => state[0].toUpperCase() + state.slice(1).toLowerCase())
].join('')
let inheritedTextColor = computedDirectives.textColor
let inheritedTextAuto = computedDirectives.textAuto
let inheritedTextOpacity = computedDirectives.textOpacity
let inheritedTextOpacityMode = computedDirectives.textOpacityMode
const lowerLevelTextSelector = [...selector.split(/ /g).slice(0, -1), soloSelector].join(' ')
const lowerLevelTextRule = computed[lowerLevelTextSelector]
if (inheritedTextColor == null || inheritedTextOpacity == null || inheritedTextOpacityMode == null) {
inheritedTextColor = computedDirectives.textColor ?? lowerLevelTextRule.textColor
inheritedTextAuto = computedDirectives.textAuto ?? lowerLevelTextRule.textAuto
inheritedTextOpacity = computedDirectives.textOpacity ?? lowerLevelTextRule.textOpacity
inheritedTextOpacityMode = computedDirectives.textOpacityMode ?? lowerLevelTextRule.textOpacityMode
}
const newTextRule = {
...computedRule,
directives: {
...computedRule.directives,
textColor: inheritedTextColor,
textAuto: inheritedTextAuto ?? 'preserve',
textOpacity: inheritedTextOpacity,
textOpacityMode: inheritedTextOpacityMode
}
}
dynamicVars.inheritedBackground = lowerLevelBackground
dynamicVars.stacked = convert(stacked[lowerLevelSelector]).rgb
const intendedTextColor = convert(findColor(inheritedTextColor, { dynamicVars, staticVars })).rgb
const textColor = newTextRule.directives.textAuto === 'no-auto'
? intendedTextColor
: getTextColor(
convert(stacked[lowerLevelSelector]).rgb,
intendedTextColor,
newTextRule.directives.textAuto === 'preserve'
)
const virtualDirectives = computed[lowerLevelSelector].virtualDirectives || {}
const virtualDirectivesRaw = computed[lowerLevelSelector].virtualDirectivesRaw || {}
// Storing color data in lower layer to use as custom css properties
virtualDirectives[virtualName] = getTextColorAlpha(newTextRule.directives, textColor, dynamicVars)
virtualDirectivesRaw[virtualName] = textColor
computed[lowerLevelSelector].virtualDirectives = virtualDirectives
computed[lowerLevelSelector].virtualDirectivesRaw = virtualDirectivesRaw
return {
dynamicVars,
selector: cssSelector.split(/ /g).slice(0, -1).join(' '),
...combination,
directives: {},
virtualDirectives: {
[virtualName]: getTextColorAlpha(newTextRule.directives, textColor, dynamicVars)
},
virtualDirectivesRaw: {
[virtualName]: textColor
}
}
} else {
computed[selector] = computed[selector] || {}
// TODO: DEFAULT TEXT COLOR
const lowerLevelStackedBackground = stacked[lowerLevelSelector] || convert(ultimateBackgroundColor).rgb
if (computedDirectives.background) {
let inheritRule = null
const variantRules = ruleset.filter(
findRules({
component: combination.component,
variant: combination.variant,
parent: combination.parent
})
)
const lastVariantRule = variantRules[variantRules.length - 1]
if (lastVariantRule) {
inheritRule = lastVariantRule
} else {
const normalRules = ruleset.filter(findRules({
component: combination.component,
parent: combination.parent
}))
const lastNormalRule = normalRules[normalRules.length - 1]
inheritRule = lastNormalRule
}
const inheritSelector = ruleToSelector({ ...inheritRule, parent: combination.parent }, true)
const inheritedBackground = computed[inheritSelector].background
dynamicVars.inheritedBackground = inheritedBackground
const rgb = convert(findColor(computedDirectives.background, { dynamicVars, staticVars })).rgb
if (!stacked[selector]) {
let blend
const alpha = computedDirectives.opacity ?? 1
if (alpha >= 1) {
blend = rgb
} else if (alpha <= 0) {
blend = lowerLevelStackedBackground
} else {
blend = alphaBlend(rgb, computedDirectives.opacity, lowerLevelStackedBackground)
}
stacked[selector] = blend
computed[selector].background = { ...rgb, a: computedDirectives.opacity ?? 1 }
}
}
if (computedDirectives.shadow) {
dynamicVars.shadow = flattenDeep(findShadow(flattenDeep(computedDirectives.shadow), { dynamicVars, staticVars }))
}
if (!stacked[selector]) {
computedDirectives.background = 'transparent'
computedDirectives.opacity = 0
stacked[selector] = lowerLevelStackedBackground
computed[selector].background = { ...lowerLevelStackedBackground, a: 0 }
}
dynamicVars.stacked = stacked[selector]
dynamicVars.background = computed[selector].background
const dynamicSlots = Object.entries(computedDirectives).filter(([k, v]) => k.startsWith('--'))
dynamicSlots.forEach(([k, v]) => {
const [type, value] = v.split('|').map(x => x.trim()) // woah, Extreme!
switch (type) {
case 'color': {
const color = findColor(value, { dynamicVars, staticVars })
dynamicVars[k] = color
if (combination.component === rootComponentName) {
staticVars[k.substring(2)] = color
}
break
}
case 'shadow': {
const shadow = value.split(/,/g).map(s => s.trim()).filter(x => x)
dynamicVars[k] = shadow
if (combination.component === rootComponentName) {
staticVars[k.substring(2)] = shadow
}
break
}
case 'generic': {
dynamicVars[k] = value
if (combination.component === rootComponentName) {
staticVars[k.substring(2)] = value
}
break
}
}
})
const rule = {
dynamicVars,
selector: cssSelector,
...combination,
directives: computedDirectives
}
return rule
}
} catch (e) {
const { component, variant, state } = combination
throw new Error(`Error processing combination ${component}.${variant}:${state.join(':')}: ${e}`)
} }
} }