mix.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. function appendOrSet(a, b) {
  2. if (typeof b === 'string' && /^\s*\|/.test(b)) {
  3. return typeof a === 'string'
  4. ? a + b
  5. : b.replace(/^\s*\|\s*/, '');
  6. }
  7. return b || null;
  8. }
  9. function extractProps(obj, props) {
  10. const result = Object.create(null);
  11. for (const prop of Object.keys(obj)) {
  12. if (props.includes(prop)) {
  13. result[prop] = obj[prop];
  14. }
  15. }
  16. return result;
  17. }
  18. function mergeDicts(base, ext, fields) {
  19. const result = { ...base };
  20. for (const [key, props] of Object.entries(ext)) {
  21. result[key] = {
  22. ...result[key],
  23. ...fields ? extractProps(props, fields) : props
  24. };
  25. }
  26. return result;
  27. }
  28. export default function mix(dest, src) {
  29. const result = { ...dest };
  30. for (const [prop, value] of Object.entries(src)) {
  31. switch (prop) {
  32. case 'generic':
  33. result[prop] = Boolean(value);
  34. break;
  35. case 'cssWideKeywords':
  36. result[prop] = dest[prop]
  37. ? [...dest[prop], ...value]
  38. : value || [];
  39. break;
  40. case 'units':
  41. result[prop] = { ...dest[prop] };
  42. for (const [name, patch] of Object.entries(value)) {
  43. result[prop][name] = Array.isArray(patch) ? patch : [];
  44. }
  45. break;
  46. case 'atrules':
  47. result[prop] = { ...dest[prop] };
  48. for (const [name, atrule] of Object.entries(value)) {
  49. const exists = result[prop][name] || {};
  50. const current = result[prop][name] = {
  51. prelude: exists.prelude || null,
  52. descriptors: {
  53. ...exists.descriptors
  54. }
  55. };
  56. if (!atrule) {
  57. continue;
  58. }
  59. current.prelude = atrule.prelude
  60. ? appendOrSet(current.prelude, atrule.prelude)
  61. : current.prelude || null;
  62. for (const [descriptorName, descriptorValue] of Object.entries(atrule.descriptors || {})) {
  63. current.descriptors[descriptorName] = descriptorValue
  64. ? appendOrSet(current.descriptors[descriptorName], descriptorValue)
  65. : null;
  66. }
  67. if (!Object.keys(current.descriptors).length) {
  68. current.descriptors = null;
  69. }
  70. }
  71. break;
  72. case 'types':
  73. case 'properties':
  74. result[prop] = { ...dest[prop] };
  75. for (const [name, syntax] of Object.entries(value)) {
  76. result[prop][name] = appendOrSet(result[prop][name], syntax);
  77. }
  78. break;
  79. case 'parseContext':
  80. result[prop] = {
  81. ...dest[prop],
  82. ...value
  83. };
  84. break;
  85. case 'scope':
  86. case 'features':
  87. result[prop] = mergeDicts(dest[prop], value);
  88. break;
  89. case 'atrule':
  90. case 'pseudo':
  91. result[prop] = mergeDicts(dest[prop], value, ['parse']);
  92. break;
  93. case 'node':
  94. result[prop] = mergeDicts(dest[prop], value, ['name', 'structure', 'parse', 'generate', 'walkContext']);
  95. break;
  96. }
  97. }
  98. return result;
  99. }