mix.js 3.7 KB

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