| const hasOwnProperty = Object.prototype.hasOwnProperty; | 
| const shape = { | 
|     generic: true, | 
|     types: appendOrAssign, | 
|     atrules: { | 
|         prelude: appendOrAssignOrNull, | 
|         descriptors: appendOrAssignOrNull | 
|     }, | 
|     properties: appendOrAssign, | 
|     parseContext: assign, | 
|     scope: deepAssign, | 
|     atrule: ['parse'], | 
|     pseudo: ['parse'], | 
|     node: ['name', 'structure', 'parse', 'generate', 'walkContext'] | 
| }; | 
|   | 
| function isObject(value) { | 
|     return value && value.constructor === Object; | 
| } | 
|   | 
| function copy(value) { | 
|     return isObject(value) | 
|         ? Object.assign({}, value) | 
|         : value; | 
| } | 
|   | 
| function assign(dest, src) { | 
|     return Object.assign(dest, src); | 
| } | 
|   | 
| function deepAssign(dest, src) { | 
|     for (const key in src) { | 
|         if (hasOwnProperty.call(src, key)) { | 
|             if (isObject(dest[key])) { | 
|                 deepAssign(dest[key], copy(src[key])); | 
|             } else { | 
|                 dest[key] = copy(src[key]); | 
|             } | 
|         } | 
|     } | 
|   | 
|     return dest; | 
| } | 
|   | 
| function append(a, b) { | 
|     if (typeof b === 'string' && /^\s*\|/.test(b)) { | 
|         return typeof a === 'string' | 
|             ? a + b | 
|             : b.replace(/^\s*\|\s*/, ''); | 
|     } | 
|   | 
|     return b || null; | 
| } | 
|   | 
| function appendOrAssign(a, b) { | 
|     if (typeof b === 'string') { | 
|         return append(a, b); | 
|     } | 
|   | 
|     const result = Object.assign({}, a); | 
|     for (let key in b) { | 
|         if (hasOwnProperty.call(b, key)) { | 
|             result[key] = append(hasOwnProperty.call(a, key) ? a[key] : undefined, b[key]); | 
|         } | 
|     } | 
|   | 
|     return result; | 
| } | 
|   | 
| function appendOrAssignOrNull(a, b) { | 
|     const result = appendOrAssign(a, b); | 
|   | 
|     return !isObject(result) || Object.keys(result).length | 
|         ? result | 
|         : null; | 
| } | 
|   | 
| function mix(dest, src, shape) { | 
|     for (const key in shape) { | 
|         if (hasOwnProperty.call(shape, key) === false) { | 
|             continue; | 
|         } | 
|   | 
|         if (shape[key] === true) { | 
|             if (key in src) { | 
|                 if (hasOwnProperty.call(src, key)) { | 
|                     dest[key] = copy(src[key]); | 
|                 } | 
|             } | 
|         } else if (shape[key]) { | 
|             if (typeof shape[key] === 'function') { | 
|                 const fn = shape[key]; | 
|                 dest[key] = fn({}, dest[key]); | 
|                 dest[key] = fn(dest[key] || {}, src[key]); | 
|             } else if (isObject(shape[key])) { | 
|                 const result = {}; | 
|   | 
|                 for (let name in dest[key]) { | 
|                     result[name] = mix({}, dest[key][name], shape[key]); | 
|                 } | 
|   | 
|                 for (let name in src[key]) { | 
|                     result[name] = mix(result[name] || {}, src[key][name], shape[key]); | 
|                 } | 
|   | 
|                 dest[key] = result; | 
|             } else if (Array.isArray(shape[key])) { | 
|                 const res = {}; | 
|                 const innerShape = shape[key].reduce(function(s, k) { | 
|                     s[k] = true; | 
|                     return s; | 
|                 }, {}); | 
|   | 
|                 for (const [name, value] of Object.entries(dest[key] || {})) { | 
|                     res[name] = {}; | 
|                     if (value) { | 
|                         mix(res[name], value, innerShape); | 
|                     } | 
|                 } | 
|   | 
|                 for (const name in src[key]) { | 
|                     if (hasOwnProperty.call(src[key], name)) { | 
|                         if (!res[name]) { | 
|                             res[name] = {}; | 
|                         } | 
|   | 
|                         if (src[key] && src[key][name]) { | 
|                             mix(res[name], src[key][name], innerShape); | 
|                         } | 
|                     } | 
|                 } | 
|   | 
|                 dest[key] = res; | 
|             } | 
|         } | 
|     } | 
|     return dest; | 
| } | 
|   | 
| module.exports = (dest, src) => mix(dest, src, shape); |