You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
115 lines
3.5 KiB
115 lines
3.5 KiB
function appendOrSet(a, b) { |
|
if (typeof b === 'string' && /^\s*\|/.test(b)) { |
|
return typeof a === 'string' |
|
? a + b |
|
: b.replace(/^\s*\|\s*/, ''); |
|
} |
|
|
|
return b || null; |
|
} |
|
|
|
function sliceProps(obj, props) { |
|
const result = Object.create(null); |
|
|
|
for (const [key, value] of Object.entries(obj)) { |
|
if (value) { |
|
result[key] = {}; |
|
for (const prop of Object.keys(value)) { |
|
if (props.includes(prop)) { |
|
result[key][prop] = value[prop]; |
|
} |
|
} |
|
} |
|
} |
|
|
|
return result; |
|
} |
|
|
|
export default function mix(dest, src) { |
|
const result = { ...dest }; |
|
|
|
for (const [prop, value] of Object.entries(src)) { |
|
switch (prop) { |
|
case 'generic': |
|
result[prop] = Boolean(value); |
|
break; |
|
|
|
case 'units': |
|
result[prop] = { ...dest[prop] }; |
|
for (const [name, patch] of Object.entries(value)) { |
|
result[prop][name] = Array.isArray(patch) ? patch : []; |
|
} |
|
break; |
|
|
|
case 'atrules': |
|
result[prop] = { ...dest[prop] }; |
|
|
|
for (const [name, atrule] of Object.entries(value)) { |
|
const exists = result[prop][name] || {}; |
|
const current = result[prop][name] = { |
|
prelude: exists.prelude || null, |
|
descriptors: { |
|
...exists.descriptors |
|
} |
|
}; |
|
|
|
if (!atrule) { |
|
continue; |
|
} |
|
|
|
current.prelude = atrule.prelude |
|
? appendOrSet(current.prelude, atrule.prelude) |
|
: current.prelude || null; |
|
|
|
for (const [descriptorName, descriptorValue] of Object.entries(atrule.descriptors || {})) { |
|
current.descriptors[descriptorName] = descriptorValue |
|
? appendOrSet(current.descriptors[descriptorName], descriptorValue) |
|
: null; |
|
} |
|
|
|
if (!Object.keys(current.descriptors).length) { |
|
current.descriptors = null; |
|
} |
|
} |
|
break; |
|
|
|
case 'types': |
|
case 'properties': |
|
result[prop] = { ...dest[prop] }; |
|
for (const [name, syntax] of Object.entries(value)) { |
|
result[prop][name] = appendOrSet(result[prop][name], syntax); |
|
} |
|
break; |
|
|
|
case 'scope': |
|
result[prop] = { ...dest[prop] }; |
|
for (const [name, props] of Object.entries(value)) { |
|
result[prop][name] = { ...result[prop][name], ...props }; |
|
} |
|
break; |
|
|
|
case 'parseContext': |
|
result[prop] = { |
|
...dest[prop], |
|
...value |
|
}; |
|
break; |
|
|
|
case 'atrule': |
|
case 'pseudo': |
|
result[prop] = { |
|
...dest[prop], |
|
...sliceProps(value, ['parse']) }; |
|
break; |
|
|
|
case 'node': |
|
result[prop] = { |
|
...dest[prop], |
|
...sliceProps(value, ['name', 'structure', 'parse', 'generate', 'walkContext']) |
|
}; |
|
break; |
|
} |
|
} |
|
|
|
return result; |
|
}
|
|
|