mix.cjs 3.6 KB

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