141 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			141 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 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);
 |