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); 
 |