@luna_ui/luna 0.3.3 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/dist/cli.mjs +1264 -27
  2. package/dist/css/index.d.ts +194 -0
  3. package/dist/css/index.js +721 -0
  4. package/dist/css/runtime.d.ts +92 -0
  5. package/dist/css/runtime.js +179 -0
  6. package/dist/index.js +1 -1
  7. package/dist/jsx-dev-runtime.js +1 -1
  8. package/dist/jsx-runtime.d.ts +5 -0
  9. package/dist/jsx-runtime.js +1 -1
  10. package/dist/src-CHiGeWfy.js +1 -0
  11. package/dist/vite-plugin.d.ts +122 -0
  12. package/dist/vite-plugin.js +1518 -0
  13. package/package.json +16 -2
  14. package/src/css/extract.ts +798 -0
  15. package/src/css/index.ts +10 -0
  16. package/src/css/inject.ts +205 -0
  17. package/src/css/inline.ts +182 -0
  18. package/src/css/minify.ts +70 -0
  19. package/src/css/optimizer.ts +6 -0
  20. package/src/css/runtime.ts +344 -0
  21. package/src/css-optimizer/README.md +353 -0
  22. package/src/css-optimizer/cooccurrence.ts +100 -0
  23. package/src/css-optimizer/core.ts +263 -0
  24. package/src/css-optimizer/extractors.ts +243 -0
  25. package/src/css-optimizer/hash.ts +54 -0
  26. package/src/css-optimizer/index.ts +129 -0
  27. package/src/css-optimizer/merge.ts +109 -0
  28. package/src/css-optimizer/moonbit-analyzer.ts +210 -0
  29. package/src/css-optimizer/parser.ts +120 -0
  30. package/src/css-optimizer/pattern.ts +171 -0
  31. package/src/css-optimizer/transformers.ts +301 -0
  32. package/src/css-optimizer/types.ts +128 -0
  33. package/src/event-utils.ts +227 -0
  34. package/src/index.ts +890 -0
  35. package/src/jsx-dev-runtime.ts +2 -0
  36. package/src/jsx-runtime.ts +398 -0
  37. package/src/vite-plugin.ts +718 -0
  38. package/tests/__screenshots__/context.test.ts/Context-API-context-with-reactive-effects-context-value-accessible-in-effect-1.png +0 -0
  39. package/tests/__screenshots__/dom.test.ts/DOM-API-For-component--SolidJS-style--For-updates-when-signal-changes-1.png +0 -0
  40. package/tests/__screenshots__/dom.test.ts/DOM-API-Show-component--SolidJS-style--Show-accepts-children-as-function-1.png +0 -0
  41. package/tests/__screenshots__/dom.test.ts/DOM-API-Show-component--SolidJS-style--Show-toggles-visibility-1.png +0 -0
  42. package/tests/__screenshots__/dom.test.ts/DOM-API-createElement-createElement-with-dynamic-attribute-1.png +0 -0
  43. package/tests/__screenshots__/dom.test.ts/DOM-API-createElement-createElement-with-dynamic-style-1.png +0 -0
  44. package/tests/__screenshots__/dom.test.ts/DOM-API-createElementNs--SVG-support--createElementNs-with-dynamic-attribute-1.png +0 -0
  45. package/tests/__screenshots__/dom.test.ts/DOM-API-effect-with-DOM-effect-tracks-signal-changes-1.png +0 -0
  46. package/tests/__screenshots__/dom.test.ts/DOM-API-forEach--list-rendering--forEach-handles-clear-to-empty-1.png +0 -0
  47. package/tests/__screenshots__/dom.test.ts/DOM-API-forEach--list-rendering--forEach-handles-empty-array-1.png +0 -0
  48. package/tests/__screenshots__/dom.test.ts/DOM-API-forEach--list-rendering--forEach-removes-items-1.png +0 -0
  49. package/tests/__screenshots__/dom.test.ts/DOM-API-forEach--list-rendering--forEach-renders-initial-list-1.png +0 -0
  50. package/tests/__screenshots__/dom.test.ts/DOM-API-forEach--list-rendering--forEach-updates-when-items-change-1.png +0 -0
  51. package/tests/__screenshots__/dom.test.ts/DOM-API-forEach-with-SVG-elements-forEach-handles-empty-to-non-empty-transition-in-SVG-1.png +0 -0
  52. package/tests/__screenshots__/dom.test.ts/DOM-API-forEach-with-SVG-elements-forEach-handles-reordering-in-SVG-1.png +0 -0
  53. package/tests/__screenshots__/dom.test.ts/DOM-API-forEach-with-SVG-elements-forEach-updates-SVG-elements-when-signal-changes-1.png +0 -0
  54. package/tests/__screenshots__/dom.test.ts/DOM-API-forEach-with-SVG-elements-forEach-with-nested-SVG-groups-1.png +0 -0
  55. package/tests/__screenshots__/dom.test.ts/DOM-API-ref-callback--JSX-style--ref-callback-with-nested-elements-1.png +0 -0
  56. package/tests/__screenshots__/dom.test.ts/DOM-API-show--conditional-rendering--show-creates-a-node-1.png +0 -0
  57. package/tests/__screenshots__/dom.test.ts/DOM-API-show--conditional-rendering--show-with-false-condition-creates-placeholder-1.png +0 -0
  58. package/tests/__screenshots__/dom.test.ts/DOM-API-text-nodes-textDyn-creates-reactive-text-node-1.png +0 -0
  59. package/tests/__screenshots__/integration.test.ts/Integration--Nested-Components-with-Context-Complex-nested-scenario-forEach-renders-correctly-without-show--initial-items--1.png +0 -0
  60. package/tests/__screenshots__/integration.test.ts/Integration--Nested-Components-with-Context-Complex-nested-scenario-forEach-with-context-renders-correctly-without-show-1.png +0 -0
  61. package/tests/__screenshots__/integration.test.ts/Integration--Nested-Components-with-Context-Complex-nested-scenario-nested-components-with-context--forEach--and-show-1.png +0 -0
  62. package/tests/__screenshots__/integration.test.ts/Integration--Nested-Components-with-Context-Complex-nested-scenario-show-and-forEach-inherit-context-from-Owner--fixed--1.png +0 -0
  63. package/tests/__screenshots__/integration.test.ts/Integration--Nested-Components-with-Context-Complex-nested-scenario-show-and-forEach-work-together--context-uses-default--1.png +0 -0
  64. package/tests/__screenshots__/integration.test.ts/Integration--Nested-Components-with-Context-Context---ForEach-integration-forEach-items-can-access-context-1.png +0 -0
  65. package/tests/__screenshots__/integration.test.ts/Integration--Nested-Components-with-Context-ForEach-with-reactive-updates-forEach-renders-initial-list-1.png +0 -0
  66. package/tests/__screenshots__/integration.test.ts/Integration--Nested-Components-with-Context-ForEach-with-reactive-updates-forEach-updates-when-signal-changes-1.png +0 -0
  67. package/tests/__screenshots__/integration.test.ts/Integration--Nested-Components-with-Context-ForEach-with-reactive-updates-forEach-with-object-items-1.png +0 -0
  68. package/tests/__screenshots__/integration.test.ts/Integration--Nested-Components-with-Context-Show--conditional-rendering--show-hides-when-condition-is-false-1.png +0 -0
  69. package/tests/__screenshots__/integration.test.ts/Integration--Nested-Components-with-Context-Show--conditional-rendering--show-renders-when-condition-is-true-1.png +0 -0
  70. package/tests/__screenshots__/integration.test.ts/Integration--Nested-Components-with-Context-Show--conditional-rendering--show-toggles-from-false-to-true-1.png +0 -0
  71. package/tests/__screenshots__/integration.test.ts/Integration--Nested-Components-with-Context-Show--conditional-rendering--show-toggles-reactively-1.png +0 -0
  72. package/tests/__screenshots__/lifecycle.test.ts/onCleanup-in-Component-Body--Solid-js-style--event-listener-pattern--Solid-js-docs-example--1.png +0 -0
  73. package/tests/__screenshots__/lifecycle.test.ts/onCleanup-in-Component-Body--Solid-js-style--multiple-cleanups-in-component-body--LIFO-order--1.png +0 -0
  74. package/tests/__screenshots__/lifecycle.test.ts/onCleanup-in-Component-Body--Solid-js-style--onCleanup-in-component-body-runs-on-unmount-1.png +0 -0
  75. package/tests/__screenshots__/lifecycle.test.ts/onCleanup-in-Component-Body--Solid-js-style--onCleanup-works-with-For-loop-items--component-body-style--1.png +0 -0
  76. package/tests/__screenshots__/lifecycle.test.ts/onCleanup-in-Component-Body--Solid-js-style--timer-cleanup-pattern--Solid-js-style--1.png +0 -0
  77. package/tests/__screenshots__/lifecycle.test.ts/onCleanup-in-Effects-effect-cleanup-runs-before-re-run-1.png +0 -0
  78. package/tests/__screenshots__/preact-signals-comparison.test.ts/Bulk-Updates-large-list-update-1.png +0 -0
  79. package/tests/__screenshots__/preact-signals-comparison.test.ts/Bulk-Updates-nested-batch-operations-1.png +0 -0
  80. package/tests/__screenshots__/preact-signals-comparison.test.ts/Bulk-Updates-rapid-sequential-updates-1.png +0 -0
  81. package/tests/__screenshots__/preact-signals-comparison.test.ts/DOM-Rendering-Comparison---Conditional-Show-component---visible-1.png +0 -0
  82. package/tests/__screenshots__/preact-signals-comparison.test.ts/DOM-Rendering-Comparison---Conditional-show-hide-element---visible-1.png +0 -0
  83. package/tests/__screenshots__/preact-signals-comparison.test.ts/DOM-Rendering-Comparison---Fragments-fragment-with-list-1.png +0 -0
  84. package/tests/__screenshots__/preact-signals-comparison.test.ts/DOM-Rendering-Comparison---Fragments-nested-fragments-1.png +0 -0
  85. package/tests/__screenshots__/preact-signals-comparison.test.ts/DOM-Rendering-Comparison---Fragments-simple-fragment-1.png +0 -0
  86. package/tests/__screenshots__/preact-signals-comparison.test.ts/DOM-Rendering-Comparison---Reactive-Updates-conditional-toggle-updates-1.png +0 -0
  87. package/tests/__screenshots__/preact-signals-comparison.test.ts/DOM-Rendering-Comparison---Reactive-Updates-list-addition-updates-match-1.png +0 -0
  88. package/tests/__screenshots__/preact-signals-comparison.test.ts/DOM-Rendering-Comparison---Reactive-Updates-list-removal-updates-match-1.png +0 -0
  89. package/tests/__screenshots__/preact-signals-comparison.test.ts/DOM-Rendering-Comparison---Reactive-Updates-text-updates-match-1.png +0 -0
  90. package/tests/__screenshots__/preact-signals-comparison.test.ts/Dynamic-Attributes-Comparison-dynamic-className-updates-1.png +0 -0
  91. package/tests/__screenshots__/preact-signals-comparison.test.ts/Dynamic-Attributes-Comparison-dynamic-style-updates-1.png +0 -0
  92. package/tests/__screenshots__/preact-signals-comparison.test.ts/Dynamic-Attributes-Comparison-multiple-dynamic-attributes-1.png +0 -0
  93. package/tests/__screenshots__/preact-signals-comparison.test.ts/Edge-Cases-deeply-nested-conditionals-1.png +0 -0
  94. package/tests/__screenshots__/preact-signals-comparison.test.ts/Edge-Cases-list-transitions-from-empty-to-populated-1.png +0 -0
  95. package/tests/__screenshots__/preact-signals-comparison.test.ts/Edge-Cases-list-transitions-from-populated-to-empty-1.png +0 -0
  96. package/tests/__screenshots__/preact-signals-comparison.test.ts/Effect-Cleanup-Comparison-nested-effects-cleanup-order-1.png +0 -0
  97. package/tests/__screenshots__/preact-signals-comparison.test.ts/Effect-Cleanup-Comparison-nested-effects-with-inner-signal-change-1.png +0 -0
  98. package/tests/__screenshots__/preact-signals-comparison.test.ts/Effect-Cleanup-Comparison-onCleanup-is-called-when-effect-re-runs-1.png +0 -0
  99. package/tests/__screenshots__/preact-signals-comparison.test.ts/Effect-Cleanup-Comparison-onCleanup-with-resource-simulation-1.png +0 -0
  100. package/tests/__screenshots__/preact-signals-comparison.test.ts/Fragment-Comparison-with-Preact-Fragment-with-multiple-children--no-wrapper--1.png +0 -0
  101. package/tests/__screenshots__/preact-signals-comparison.test.ts/Fragment-Comparison-with-Preact-Fragment-with-no-children-1.png +0 -0
  102. package/tests/__screenshots__/preact-signals-comparison.test.ts/Fragment-Comparison-with-Preact-fragment-with-list-1.png +0 -0
  103. package/tests/__screenshots__/preact-signals-comparison.test.ts/Fragment-Comparison-with-Preact-nested-Fragments-work-correctly-1.png +0 -0
  104. package/tests/__screenshots__/preact-signals-comparison.test.ts/List-Reordering-complex-reordering-with-additions-and-removals-1.png +0 -0
  105. package/tests/__screenshots__/preact-signals-comparison.test.ts/List-Reordering-insert-in-middle-1.png +0 -0
  106. package/tests/__screenshots__/preact-signals-comparison.test.ts/List-Reordering-remove-from-middle-1.png +0 -0
  107. package/tests/__screenshots__/preact-signals-comparison.test.ts/List-Reordering-reverse-list-order-1.png +0 -0
  108. package/tests/__screenshots__/preact-signals-comparison.test.ts/List-Reordering-shuffle-list-1.png +0 -0
  109. package/tests/__screenshots__/preact-signals-comparison.test.ts/Luna-Conditional-Rendering-Show-component-renders-when-condition-is-true-1.png +0 -0
  110. package/tests/__screenshots__/preact-signals-comparison.test.ts/Luna-Conditional-Rendering-show-renders-content-when-initially-true-1.png +0 -0
  111. package/tests/__screenshots__/preact-signals-comparison.test.ts/Luna-Conditional-Rendering-show-toggles-visibility-dynamically-1.png +0 -0
  112. package/tests/__screenshots__/preact-signals-comparison.test.ts/Memo-Dependency-Chain-conditional-memo-dependencies-1.png +0 -0
  113. package/tests/__screenshots__/preact-signals-comparison.test.ts/Signal-Behavior-Comparison-basic-signal-get-set-produces-same-values-1.png +0 -0
  114. package/tests/__screenshots__/preact-signals-comparison.test.ts/Signal-Behavior-Comparison-batch-updates-produce-same-final-values-1.png +0 -0
  115. package/tests/__screenshots__/preact-signals-comparison.test.ts/Untrack-and-Peek-Behavior-peek-reads-value-without-tracking-1.png +0 -0
  116. package/tests/__screenshots__/preact-signals-comparison.test.ts/Untrack-and-Peek-Behavior-selective-tracking-with-untrack-1.png +0 -0
  117. package/tests/__screenshots__/preact-signals-comparison.test.ts/Untrack-and-Peek-Behavior-untrack-prevents-dependency-tracking-1.png +0 -0
  118. package/tests/__screenshots__/resource.test.ts/Resource-API--SolidJS-style--reactivity-accessor-is-reactive-1.png +0 -0
  119. package/tests/__screenshots__/resource.test.ts/Resource-API-AsyncState-helpers-stateError-returns-empty-string-for-non-failure-1.png +0 -0
  120. package/tests/__screenshots__/resource.test.ts/Resource-API-AsyncState-helpers-stateError-returns-undefined-for-non-failure-1.png +0 -0
  121. package/tests/__screenshots__/resource.test.ts/Resource-API-AsyncState-helpers-stateIsFailure-and-stateError-1.png +0 -0
  122. package/tests/__screenshots__/resource.test.ts/Resource-API-AsyncState-helpers-stateIsPending-1.png +0 -0
  123. package/tests/__screenshots__/resource.test.ts/Resource-API-AsyncState-helpers-stateIsSuccess-and-stateValue-1.png +0 -0
  124. package/tests/__screenshots__/resource.test.ts/Resource-API-AsyncState-helpers-stateValue-returns-undefined-for-non-success-1.png +0 -0
  125. package/tests/__screenshots__/resource.test.ts/Resource-API-createDeferred-reject-transitions-to-failure-1.png +0 -0
  126. package/tests/__screenshots__/resource.test.ts/Resource-API-createDeferred-resolve-transitions-to-success-1.png +0 -0
  127. package/tests/__screenshots__/resource.test.ts/Resource-API-createDeferred-returns-resource--resolve--and-reject-functions-1.png +0 -0
  128. package/tests/__screenshots__/resource.test.ts/Resource-API-createDeferred-starts-in-pending-state-1.png +0 -0
  129. package/tests/__screenshots__/resource.test.ts/Resource-API-createResource-async-resolve-works-1.png +0 -0
  130. package/tests/__screenshots__/resource.test.ts/Resource-API-createResource-starts-in-pending-state-1.png +0 -0
  131. package/tests/__screenshots__/resource.test.ts/Resource-API-createResource-transitions-to-failure-on-reject-1.png +0 -0
  132. package/tests/__screenshots__/resource.test.ts/Resource-API-createResource-transitions-to-success-on-resolve-1.png +0 -0
  133. package/tests/__screenshots__/resource.test.ts/Resource-API-integration-with-Promise-can-wrap-fetch-like-async-operations-1.png +0 -0
  134. package/tests/__screenshots__/resource.test.ts/Resource-API-integration-with-Promise-works-with-setTimeout-simulation-1.png +0 -0
  135. package/tests/__screenshots__/resource.test.ts/Resource-API-resourceGet-vs-resourcePeek-resourceGet-tracks-dependencies-1.png +0 -0
  136. package/tests/__screenshots__/resource.test.ts/Resource-API-resourceGet-vs-resourcePeek-resourcePeek-does-not-track-dependencies-1.png +0 -0
  137. package/tests/__screenshots__/resource.test.ts/Resource-API-resourceRefetch-refetch-resets-to-pending-and-re-runs-fetcher-1.png +0 -0
  138. package/tests/__screenshots__/solidjs-api.test.ts/Portal-component-Portal-renders-children-to-body-by-default-1.png +0 -0
  139. package/tests/__screenshots__/solidjs-api.test.ts/Portal-component-Portal-renders-to-selector-mount-target-1.png +0 -0
  140. package/tests/__screenshots__/solidjs-api.test.ts/SolidJS-API-compatibility-createEffect-tracks-dependencies-automatically-1.png +0 -0
  141. package/tests/__screenshots__/solidjs-api.test.ts/Switch-Match-component-Switch-with-accessor-condition-in-Match-1.png +0 -0
  142. package/tests/__screenshots__/solidjs-api.test.ts/Switch-Match-component-Switch-with-multiple-Match-components-1.png +0 -0
  143. package/tests/__screenshots__/solidjs-api.test.ts/Switch-Match-component-Switch-with-single-Match-and-fallback-1.png +0 -0
  144. package/tests/__screenshots__/solidjs-api.test.ts/Switch-Match-components-Switch-updates-DOM-when-signal-changes-1.png +0 -0
  145. package/tests/__screenshots__/solidjs-api.test.ts/on---utility-on-tracks-multiple-dependencies-1.png +0 -0
  146. package/tests/__screenshots__/solidjs-api.test.ts/on---utility-on-tracks-single-dependency-1.png +0 -0
  147. package/tests/__screenshots__/solidjs-api.test.ts/on---utility-on-with-defer-option-skips-initial-run-1.png +0 -0
  148. package/tests/__screenshots__/store.test.ts/createStore-Arrays-array-updates-work-1.png +0 -0
  149. package/tests/__screenshots__/store.test.ts/createStore-Reactivity-only-triggers-when-accessed-property-changes-1.png +0 -0
  150. package/tests/__screenshots__/store.test.ts/createStore-Reactivity-parent-path-change-notifies-child-accessors-1.png +0 -0
  151. package/tests/__screenshots__/store.test.ts/createStore-Reactivity-tracks-nested-property-access-1.png +0 -0
  152. package/tests/__screenshots__/store.test.ts/createStore-Reactivity-tracks-property-access-in-effects-1.png +0 -0
  153. package/tests/context.test.ts +118 -0
  154. package/tests/css-optimizer-extractors.test.ts +264 -0
  155. package/tests/css-optimizer-integration.test.ts +566 -0
  156. package/tests/css-optimizer-transformers.test.ts +301 -0
  157. package/tests/css-optimizer.test.ts +646 -0
  158. package/tests/css-runtime.bench.ts +442 -0
  159. package/tests/css-runtime.test.ts +342 -0
  160. package/tests/dom.test.ts +872 -0
  161. package/tests/integration.test.ts +405 -0
  162. package/tests/issue-5-for-infinite-loop.test.ts +516 -0
  163. package/tests/jsx-runtime.test.tsx +393 -0
  164. package/tests/lifecycle.test.ts +833 -0
  165. package/tests/move-before.bench.ts +304 -0
  166. package/tests/preact-signals-comparison.test.ts +1608 -0
  167. package/tests/resource.test.ts +160 -0
  168. package/tests/router.test.ts +117 -0
  169. package/tests/show-initial-mount-leak.test.tsx +182 -0
  170. package/tests/solidjs-api.test.ts +659 -0
  171. package/tests/static-perf.bench.ts +64 -0
  172. package/tests/store.test.ts +263 -0
  173. package/tests/tsx-syntax.test.tsx +404 -0
  174. package/dist/src-DGWY0NYx.js +0 -1
@@ -0,0 +1,301 @@
1
+ /**
2
+ * CSS Optimizer Transformers Tests
3
+ *
4
+ * Tests for pluggable output transformation strategies.
5
+ */
6
+
7
+ import { describe, test, expect } from "vitest";
8
+ import {
9
+ HtmlTransformer,
10
+ JsxTransformer,
11
+ SvelteTransformer,
12
+ MultiTransformer,
13
+ htmlTransformer,
14
+ jsxTransformer,
15
+ svelteTransformer,
16
+ } from "../src/css-optimizer/transformers.js";
17
+
18
+ describe("HtmlTransformer", () => {
19
+ const transformer = new HtmlTransformer();
20
+
21
+ test("should apply merge map to HTML", () => {
22
+ const html = `<div class="_a _b other">content</div>`;
23
+ const mergeMap = new Map([["_a _b", "_merged"]]);
24
+
25
+ const result = transformer.transform(html, mergeMap);
26
+
27
+ expect(result).toContain("_merged");
28
+ expect(result).toContain("other");
29
+ expect(result).not.toContain("_a _b");
30
+ });
31
+
32
+ test("should return unchanged HTML for empty merge map", () => {
33
+ const html = `<div class="_a _b">content</div>`;
34
+ const mergeMap = new Map<string, string>();
35
+
36
+ const result = transformer.transform(html, mergeMap);
37
+ expect(result).toBe(html);
38
+ });
39
+
40
+ test("should handle multiple class attributes", () => {
41
+ const html = `
42
+ <div class="_a _b">first</div>
43
+ <span class="_a _b">second</span>
44
+ `;
45
+ const mergeMap = new Map([["_a _b", "_merged"]]);
46
+
47
+ const result = transformer.transform(html, mergeMap);
48
+
49
+ expect(result.match(/_merged/g)).toHaveLength(2);
50
+ });
51
+
52
+ test("should preserve non-prefixed classes", () => {
53
+ const html = `<div class="_a _b regular other-class">content</div>`;
54
+ const mergeMap = new Map([["_a _b", "_merged"]]);
55
+
56
+ const result = transformer.transform(html, mergeMap);
57
+
58
+ expect(result).toContain("_merged");
59
+ expect(result).toContain("regular");
60
+ expect(result).toContain("other-class");
61
+ });
62
+
63
+ test("should apply longer patterns first", () => {
64
+ const html = `<div class="_a _b _c">content</div>`;
65
+ const mergeMap = new Map([
66
+ ["_a _b", "_short"],
67
+ ["_a _b _c", "_long"],
68
+ ]);
69
+
70
+ const result = transformer.transform(html, mergeMap);
71
+
72
+ // Should use the longer pattern
73
+ expect(result).toContain("_long");
74
+ expect(result).not.toContain("_short");
75
+ });
76
+
77
+ test("should handle partial matches", () => {
78
+ const html = `<div class="_a _b _c">content</div>`;
79
+ const mergeMap = new Map([["_a _b", "_merged"]]);
80
+
81
+ const result = transformer.transform(html, mergeMap);
82
+
83
+ expect(result).toContain("_merged");
84
+ expect(result).toContain("_c"); // Should keep unmerged class
85
+ });
86
+
87
+ test("should handle custom prefix", () => {
88
+ const html = `<div class="tw-a tw-b other">content</div>`;
89
+ const mergeMap = new Map([["tw-a tw-b", "tw-merged"]]);
90
+
91
+ const result = transformer.transform(html, mergeMap, { classPrefix: "tw-" });
92
+
93
+ expect(result).toContain("tw-merged");
94
+ expect(result).toContain("other");
95
+ });
96
+ });
97
+
98
+ describe("JsxTransformer", () => {
99
+ const transformer = new JsxTransformer();
100
+
101
+ test("should apply merge map to JSX className", () => {
102
+ const jsx = `<div className="_a _b other">content</div>`;
103
+ const mergeMap = new Map([["_a _b", "_merged"]]);
104
+
105
+ const result = transformer.transform(jsx, mergeMap);
106
+
107
+ expect(result).toContain('className="_merged');
108
+ expect(result).toContain("other");
109
+ });
110
+
111
+ test("should handle React components", () => {
112
+ const jsx = `
113
+ function Component() {
114
+ return <div className="_x _y">content</div>;
115
+ }
116
+ `;
117
+ const mergeMap = new Map([["_x _y", "_merged"]]);
118
+
119
+ const result = transformer.transform(jsx, mergeMap);
120
+
121
+ expect(result).toContain("_merged");
122
+ });
123
+
124
+ test("should preserve JSX structure", () => {
125
+ const jsx = `<Button className="_a _b" onClick={handleClick}>Click</Button>`;
126
+ const mergeMap = new Map([["_a _b", "_merged"]]);
127
+
128
+ const result = transformer.transform(jsx, mergeMap);
129
+
130
+ expect(result).toContain("onClick={handleClick}");
131
+ expect(result).toContain('className="_merged"');
132
+ });
133
+ });
134
+
135
+ describe("SvelteTransformer", () => {
136
+ const transformer = new SvelteTransformer();
137
+
138
+ test("should apply merge map to Svelte class", () => {
139
+ const svelte = `<div class="_a _b other">content</div>`;
140
+ const mergeMap = new Map([["_a _b", "_merged"]]);
141
+
142
+ const result = transformer.transform(svelte, mergeMap);
143
+
144
+ expect(result).toContain("_merged");
145
+ expect(result).toContain("other");
146
+ });
147
+
148
+ test("should preserve dynamic expressions", () => {
149
+ const svelte = `<div class="_a _b {dynamic}">content</div>`;
150
+ const mergeMap = new Map([["_a _b", "_merged"]]);
151
+
152
+ const result = transformer.transform(svelte, mergeMap);
153
+
154
+ expect(result).toContain("_merged");
155
+ expect(result).toContain("{dynamic}");
156
+ });
157
+
158
+ test("should handle complex expressions", () => {
159
+ const svelte = `<div class="_base {isActive ? 'active' : ''} _end">content</div>`;
160
+ const mergeMap = new Map([["_base _end", "_merged"]]);
161
+
162
+ const result = transformer.transform(svelte, mergeMap);
163
+
164
+ expect(result).toContain("_merged");
165
+ expect(result).toContain("{isActive ? 'active' : ''}");
166
+ });
167
+
168
+ test("should preserve class:directive syntax", () => {
169
+ const svelte = `
170
+ <div class="_a _b" class:active={isActive}>content</div>
171
+ `;
172
+ const mergeMap = new Map([["_a _b", "_merged"]]);
173
+
174
+ const result = transformer.transform(svelte, mergeMap);
175
+
176
+ expect(result).toContain("_merged");
177
+ expect(result).toContain("class:active={isActive}");
178
+ });
179
+ });
180
+
181
+ describe("MultiTransformer", () => {
182
+ const transformer = new MultiTransformer();
183
+
184
+ test("should use HTML transformer by default", () => {
185
+ const html = `<div class="_a _b">content</div>`;
186
+ const mergeMap = new Map([["_a _b", "_merged"]]);
187
+
188
+ const result = transformer.transform(html, mergeMap);
189
+
190
+ expect(result).toContain("_merged");
191
+ });
192
+
193
+ test("should select transformer by file type", () => {
194
+ const jsx = `<div className="_x _y">content</div>`;
195
+ const mergeMap = new Map([["_x _y", "_merged"]]);
196
+
197
+ const result = transformer.transformWithType(jsx, mergeMap, "jsx");
198
+
199
+ expect(result).toContain('className="_merged');
200
+ });
201
+
202
+ test("should transform multiple files", () => {
203
+ const files = [
204
+ { content: `<div class="_a _b">html</div>`, path: "index.html" },
205
+ { content: `<div className="_a _b">jsx</div>`, path: "app.jsx" },
206
+ ];
207
+ const mergeMap = new Map([["_a _b", "_merged"]]);
208
+
209
+ const results = transformer.transformFiles(files, mergeMap);
210
+
211
+ expect(results[0].content).toContain('class="_merged');
212
+ expect(results[1].content).toContain('className="_merged');
213
+ });
214
+
215
+ test("should preserve file paths", () => {
216
+ const files = [{ content: `<div class="_a _b">content</div>`, path: "src/page.html" }];
217
+ const mergeMap = new Map([["_a _b", "_merged"]]);
218
+
219
+ const results = transformer.transformFiles(files, mergeMap);
220
+
221
+ expect(results[0].path).toBe("src/page.html");
222
+ });
223
+
224
+ test("should allow registering custom transformers", () => {
225
+ const customTransformer = new MultiTransformer();
226
+ customTransformer.register("vue", new HtmlTransformer()); // Vue uses class=""
227
+
228
+ const vue = `<template><div class="_vue _style">content</div></template>`;
229
+ const mergeMap = new Map([["_vue _style", "_merged"]]);
230
+
231
+ const result = customTransformer.transformWithType(vue, mergeMap, "vue");
232
+
233
+ expect(result).toContain("_merged");
234
+ });
235
+ });
236
+
237
+ describe("Default transformer instances", () => {
238
+ test("htmlTransformer should be a singleton", () => {
239
+ expect(htmlTransformer).toBeInstanceOf(HtmlTransformer);
240
+ });
241
+
242
+ test("jsxTransformer should be a singleton", () => {
243
+ expect(jsxTransformer).toBeInstanceOf(JsxTransformer);
244
+ });
245
+
246
+ test("svelteTransformer should be a singleton", () => {
247
+ expect(svelteTransformer).toBeInstanceOf(SvelteTransformer);
248
+ });
249
+ });
250
+
251
+ describe("Transformer edge cases", () => {
252
+ test("should handle empty content", () => {
253
+ const mergeMap = new Map([["_a _b", "_merged"]]);
254
+
255
+ expect(htmlTransformer.transform("", mergeMap)).toBe("");
256
+ expect(jsxTransformer.transform("", mergeMap)).toBe("");
257
+ expect(svelteTransformer.transform("", mergeMap)).toBe("");
258
+ });
259
+
260
+ test("should handle content without class attributes", () => {
261
+ const html = `<div>no classes here</div>`;
262
+ const mergeMap = new Map([["_a _b", "_merged"]]);
263
+
264
+ expect(htmlTransformer.transform(html, mergeMap)).toBe(html);
265
+ });
266
+
267
+ test("should handle class attributes without matches", () => {
268
+ const html = `<div class="_x _y">content</div>`;
269
+ const mergeMap = new Map([["_a _b", "_merged"]]);
270
+
271
+ const result = htmlTransformer.transform(html, mergeMap);
272
+
273
+ // Should keep original classes
274
+ expect(result).toContain("_x");
275
+ expect(result).toContain("_y");
276
+ expect(result).not.toContain("_merged");
277
+ });
278
+
279
+ test("should handle overlapping patterns correctly", () => {
280
+ const html = `<div class="_a _b _c _d">content</div>`;
281
+ const mergeMap = new Map([
282
+ ["_a _b", "_ab"],
283
+ ["_c _d", "_cd"],
284
+ ]);
285
+
286
+ const result = htmlTransformer.transform(html, mergeMap);
287
+
288
+ expect(result).toContain("_ab");
289
+ expect(result).toContain("_cd");
290
+ expect(result).not.toContain("_a ");
291
+ expect(result).not.toContain("_b ");
292
+ });
293
+
294
+ test("should preserve empty class attribute", () => {
295
+ const html = `<div class="">empty</div>`;
296
+ const mergeMap = new Map([["_a _b", "_merged"]]);
297
+
298
+ const result = htmlTransformer.transform(html, mergeMap);
299
+ expect(result).toContain('class=""');
300
+ });
301
+ });