@reventlessdev/reventless-gwt 1.0.0-alpha.74

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 (172) hide show
  1. package/CHANGELOG.md +698 -0
  2. package/LICENSE +202 -0
  3. package/bin/reventless-dev.mjs +21 -0
  4. package/package.json +54 -0
  5. package/rescript.json +36 -0
  6. package/src/.gwtignore +10 -0
  7. package/src/Automation_GWT.res +207 -0
  8. package/src/Automation_GWT.res.mjs +160 -0
  9. package/src/Behavior_GWT.res +603 -0
  10. package/src/Behavior_GWT.res.mjs +426 -0
  11. package/src/Bind.res +29 -0
  12. package/src/Bind.res.mjs +22 -0
  13. package/src/BuildClassifier.res +137 -0
  14. package/src/BuildClassifier.res.mjs +136 -0
  15. package/src/Cancellation.res +31 -0
  16. package/src/Cancellation.res.mjs +45 -0
  17. package/src/ChildProcess.res +84 -0
  18. package/src/ChildProcess.res.mjs +92 -0
  19. package/src/Cli.res +671 -0
  20. package/src/Cli.res.mjs +754 -0
  21. package/src/Collector.res +201 -0
  22. package/src/Collector.res.mjs +215 -0
  23. package/src/ComponentMeta.res +104 -0
  24. package/src/ComponentMeta.res.mjs +120 -0
  25. package/src/ComponentScan.res +103 -0
  26. package/src/ComponentScan.res.mjs +105 -0
  27. package/src/Delegate_GWT.res +303 -0
  28. package/src/Delegate_GWT.res.mjs +372 -0
  29. package/src/Diff.res +91 -0
  30. package/src/Diff.res.mjs +98 -0
  31. package/src/Discovery.res +105 -0
  32. package/src/Discovery.res.mjs +103 -0
  33. package/src/DomainDeadCode.res +93 -0
  34. package/src/DomainDeadCode.res.mjs +69 -0
  35. package/src/DomainGraph.res +0 -0
  36. package/src/DomainGraph.res.mjs +0 -0
  37. package/src/EventMapping_GWT.res +29 -0
  38. package/src/EventMapping_GWT.res.mjs +44 -0
  39. package/src/Filter.res +36 -0
  40. package/src/Filter.res.mjs +40 -0
  41. package/src/Flow_GWT.res +664 -0
  42. package/src/Flow_GWT.res.mjs +604 -0
  43. package/src/FormatterHuman.res +138 -0
  44. package/src/FormatterHuman.res.mjs +122 -0
  45. package/src/FormatterJson.res +257 -0
  46. package/src/FormatterJson.res.mjs +320 -0
  47. package/src/FormatterJunit.res +93 -0
  48. package/src/FormatterJunit.res.mjs +90 -0
  49. package/src/FormatterTap.res +139 -0
  50. package/src/FormatterTap.res.mjs +149 -0
  51. package/src/FormatterVsCode.res +362 -0
  52. package/src/FormatterVsCode.res.mjs +564 -0
  53. package/src/Hint.res +119 -0
  54. package/src/Hint.res.mjs +124 -0
  55. package/src/InboundTranslation_GWT.res +83 -0
  56. package/src/InboundTranslation_GWT.res.mjs +94 -0
  57. package/src/JestBind.res +70 -0
  58. package/src/JestBind.res.mjs +63 -0
  59. package/src/Loader.res +24 -0
  60. package/src/Loader.res.mjs +23 -0
  61. package/src/LocalHost.res +135 -0
  62. package/src/LocalHost.res.mjs +127 -0
  63. package/src/Mapping_GWT.res +474 -0
  64. package/src/Mapping_GWT.res.mjs +322 -0
  65. package/src/MultiSourceProjection_GWT.res +316 -0
  66. package/src/MultiSourceProjection_GWT.res.mjs +354 -0
  67. package/src/OutboundTranslation_GWT.res +238 -0
  68. package/src/OutboundTranslation_GWT.res.mjs +186 -0
  69. package/src/Outcome.res +161 -0
  70. package/src/Outcome.res.mjs +140 -0
  71. package/src/PackageScan.res +80 -0
  72. package/src/PackageScan.res.mjs +81 -0
  73. package/src/PlatformRunner.res +160 -0
  74. package/src/PlatformRunner.res.mjs +155 -0
  75. package/src/PlatformScan.res +138 -0
  76. package/src/PlatformScan.res.mjs +131 -0
  77. package/src/ProcessManager.res +91 -0
  78. package/src/ProcessManager.res.mjs +80 -0
  79. package/src/Projection_GWT.res +312 -0
  80. package/src/Projection_GWT.res.mjs +332 -0
  81. package/src/Query_GWT.res +379 -0
  82. package/src/Query_GWT.res.mjs +367 -0
  83. package/src/RenderRescript.res +125 -0
  84. package/src/RenderRescript.res.mjs +141 -0
  85. package/src/RunnerTypes.res +68 -0
  86. package/src/RunnerTypes.res.mjs +60 -0
  87. package/src/SideEffect_GWT.res +157 -0
  88. package/src/SideEffect_GWT.res.mjs +113 -0
  89. package/src/StateChange/ExternalAddCategorySlice.res +25 -0
  90. package/src/StateChange/ExternalAddCategorySlice.res.mjs +34 -0
  91. package/src/StateChange/ExternalAddCategorySlice_Behavior.res +19 -0
  92. package/src/StateChange/ExternalAddCategorySlice_Behavior.res.mjs +46 -0
  93. package/src/StateChange/WithFixtures.res +22 -0
  94. package/src/StateChange/WithFixtures.res.mjs +31 -0
  95. package/src/StateChange/WithFixtures_Behavior.res +17 -0
  96. package/src/StateChange/WithFixtures_Behavior.res.mjs +37 -0
  97. package/src/StateChange/WithManualOpen.res +23 -0
  98. package/src/StateChange/WithManualOpen.res.mjs +31 -0
  99. package/src/StateChange/WithManualOpen_Behavior.res +17 -0
  100. package/src/StateChange/WithManualOpen_Behavior.res.mjs +37 -0
  101. package/src/StubRuntime.res +53 -0
  102. package/src/StubRuntime.res.mjs +54 -0
  103. package/src/TestFixtures.res +16 -0
  104. package/src/TestFixtures.res.mjs +34 -0
  105. package/src/Watch.res +92 -0
  106. package/src/Watch.res.mjs +83 -0
  107. package/src/WatcherProbe.res +48 -0
  108. package/src/WatcherProbe.res.mjs +46 -0
  109. package/tests/AutomationGwtTest.res +71 -0
  110. package/tests/AutomationGwtTest.res.mjs +189 -0
  111. package/tests/BuildClassifierTest.res +94 -0
  112. package/tests/BuildClassifierTest.res.mjs +132 -0
  113. package/tests/ComponentMetaTest.res +52 -0
  114. package/tests/ComponentMetaTest.res.mjs +57 -0
  115. package/tests/DelegateGwtTest.res +487 -0
  116. package/tests/DelegateGwtTest.res.mjs +891 -0
  117. package/tests/DiscoveryTest.res +42 -0
  118. package/tests/DiscoveryTest.res.mjs +43 -0
  119. package/tests/DomainDeadCodeTest.res +116 -0
  120. package/tests/DomainDeadCodeTest.res.mjs +155 -0
  121. package/tests/DomainGraphTest.res +365 -0
  122. package/tests/DomainGraphTest.res.mjs +413 -0
  123. package/tests/FlowAggregateGwtTest.res +169 -0
  124. package/tests/FlowAggregateGwtTest.res.mjs +269 -0
  125. package/tests/FlowCrossPluginGwtTest.res +340 -0
  126. package/tests/FlowCrossPluginGwtTest.res.mjs +798 -0
  127. package/tests/FlowGwtTest.res +222 -0
  128. package/tests/FlowGwtTest.res.mjs +432 -0
  129. package/tests/FormatterVsCodeMessageTest.res +41 -0
  130. package/tests/FormatterVsCodeMessageTest.res.mjs +42 -0
  131. package/tests/InboundTranslationGwtTest.res +34 -0
  132. package/tests/InboundTranslationGwtTest.res.mjs +98 -0
  133. package/tests/LocalHostIntegration.res +70 -0
  134. package/tests/LocalHostIntegration.res.mjs +64 -0
  135. package/tests/LocalHostTest.res +71 -0
  136. package/tests/LocalHostTest.res.mjs +63 -0
  137. package/tests/MappingGwtTest.res +348 -0
  138. package/tests/MappingGwtTest.res.mjs +592 -0
  139. package/tests/OutboundTranslationGwtTest.res +59 -0
  140. package/tests/OutboundTranslationGwtTest.res.mjs +138 -0
  141. package/tests/PlatformRunnerTest.res +66 -0
  142. package/tests/PlatformRunnerTest.res.mjs +85 -0
  143. package/tests/PlatformScanTest.res +61 -0
  144. package/tests/PlatformScanTest.res.mjs +56 -0
  145. package/tests/QueryGwtTest.res +161 -0
  146. package/tests/QueryGwtTest.res.mjs +314 -0
  147. package/tests/QueryResolverGwtTest.res +71 -0
  148. package/tests/QueryResolverGwtTest.res.mjs +136 -0
  149. package/tests/RunnerUnitTest.res +219 -0
  150. package/tests/RunnerUnitTest.res.mjs +244 -0
  151. package/tests/SideEffect/FakeOrderNotification.res +23 -0
  152. package/tests/SideEffect/FakeOrderNotification.res.mjs +36 -0
  153. package/tests/SideEffect/FakeOrderNotificationGwtTest.res +35 -0
  154. package/tests/SideEffect/FakeOrderNotificationGwtTest.res.mjs +73 -0
  155. package/tests/SideEffect/MockEmail.res +18 -0
  156. package/tests/SideEffect/MockEmail.res.mjs +47 -0
  157. package/tests/StateChange/ExternalAddCategorySlice_GWT.res +27 -0
  158. package/tests/StateChange/ExternalAddCategorySlice_GWT.res.mjs +70 -0
  159. package/tests/StateChange/WithFixtures_Fixtures.res +9 -0
  160. package/tests/StateChange/WithFixtures_Fixtures.res.mjs +20 -0
  161. package/tests/StateChange/WithFixtures_GWT.res +23 -0
  162. package/tests/StateChange/WithFixtures_GWT.res.mjs +56 -0
  163. package/tests/StateChange/WithManualOpen_Fixtures.res +7 -0
  164. package/tests/StateChange/WithManualOpen_Fixtures.res.mjs +20 -0
  165. package/tests/StateChange/WithManualOpen_GWT.res +17 -0
  166. package/tests/StateChange/WithManualOpen_GWT.res.mjs +56 -0
  167. package/tests/StateChangeSliceGwtTest.res +160 -0
  168. package/tests/StateChangeSliceGwtTest.res.mjs +280 -0
  169. package/tests/StateViewSliceGwtTest.res +64 -0
  170. package/tests/StateViewSliceGwtTest.res.mjs +161 -0
  171. package/tests/WatchDebounceTest.res +78 -0
  172. package/tests/WatchDebounceTest.res.mjs +97 -0
@@ -0,0 +1,201 @@
1
+ // Module-level collector used by the CLI runner.
2
+ //
3
+ // When the CLI loads a test file via dynamic import, each `describe`/`test`
4
+ // call routes through `Bind` or `JestBind` and pushes an entry into this
5
+ // collector. After the import resolves (all registrations complete), the CLI
6
+ // drains the entries, executes each body asynchronously, and feeds the results
7
+ // into a formatter.
8
+ //
9
+ // Activation is toggled by the CLI — when inactive, `JestBind` falls back to
10
+ // Jest globals, so existing Jest test runs are unaffected.
11
+
12
+ type status = Skipped | Runnable | Only
13
+
14
+ type location = {
15
+ file: string,
16
+ line: int,
17
+ column: int,
18
+ }
19
+
20
+ type entry = {
21
+ id: string,
22
+ name: string,
23
+ describePath: array<string>,
24
+ slice: option<string>,
25
+ body: unit => promise<Outcome.outcome>,
26
+ status: status,
27
+ location: option<location>,
28
+ }
29
+
30
+ let active = ref(false)
31
+ let describeStack: ref<array<string>> = ref([])
32
+ let entries: ref<array<entry>> = ref([])
33
+ let hasOnly = ref(false)
34
+ let skipNext = ref(false)
35
+ let onlyNext = ref(false)
36
+ let skipDepth = ref(0)
37
+ let currentFile: ref<option<string>> = ref(None)
38
+
39
+ let activate = () => {
40
+ active := true
41
+ describeStack := []
42
+ entries := []
43
+ hasOnly := false
44
+ skipNext := false
45
+ onlyNext := false
46
+ currentFile := None
47
+ }
48
+
49
+ let deactivate = () => active := false
50
+ let isActive = () => active.contents
51
+
52
+ let setCurrentFile = path => currentFile := Some(path)
53
+ let getCurrentFile = () => currentFile.contents
54
+
55
+ let markSkipNext = () => skipNext := true
56
+ let markOnlyNext = () => {
57
+ onlyNext := true
58
+ hasOnly := true
59
+ }
60
+
61
+ let pushDescribe = (label: string, body: unit => unit) => {
62
+ describeStack := Array.concat(describeStack.contents, [label])
63
+ body()
64
+ let ds = describeStack.contents
65
+ describeStack := ds->Array.slice(~start=0, ~end=ds->Array.length - 1)
66
+ }
67
+
68
+ let buildId = (describePath, name) => {
69
+ let prefix = describePath->Array.join("::")
70
+ prefix == "" ? name : prefix ++ "::" ++ name
71
+ }
72
+
73
+ let push = (
74
+ ~slice=?,
75
+ ~location=?,
76
+ name: string,
77
+ body: unit => promise<Outcome.outcome>,
78
+ ) => {
79
+ let status = if skipDepth.contents > 0 {
80
+ Skipped
81
+ } else if skipNext.contents {
82
+ skipNext := false
83
+ Skipped
84
+ } else if onlyNext.contents {
85
+ onlyNext := false
86
+ Only
87
+ } else {
88
+ Runnable
89
+ }
90
+ let describePath = describeStack.contents
91
+ let id = buildId(describePath, name)
92
+ let entry = {
93
+ id,
94
+ name,
95
+ describePath,
96
+ slice,
97
+ body,
98
+ status,
99
+ location,
100
+ }
101
+ entries := Array.concat(entries.contents, [entry])
102
+ }
103
+
104
+ // A `todo` placeholder: a spec-less slice still ships a discoverable, compiling
105
+ // `_GWT.res`. Register it as a Skipped entry with a no-op passing body so the
106
+ // CLI runner lists it without executing or failing.
107
+ let pushTodo = (name: string) => {
108
+ let describePath = describeStack.contents
109
+ let id = buildId(describePath, name)
110
+ let entry = {
111
+ id,
112
+ name,
113
+ describePath,
114
+ slice: None,
115
+ body: () => Promise.resolve(Outcome.pass),
116
+ status: Skipped,
117
+ location: None,
118
+ }
119
+ entries := Array.concat(entries.contents, [entry])
120
+ }
121
+
122
+ let drain = () => {
123
+ let result = entries.contents
124
+ entries := []
125
+ describeStack := []
126
+ result
127
+ }
128
+
129
+ // Parse a single V8 stack frame line like:
130
+ // " at body (/abs/path/File.res.mjs:42:13)"
131
+ // " at /abs/path/File.res.mjs:42:13"
132
+ // Returns (file, line, column) if the frame belongs to a non-internal user file.
133
+ let parseFrame = (frame: string): option<location> => {
134
+ let trimmed = frame->String.trim
135
+ let body = if String.startsWith(trimmed, "at ") {
136
+ String.slice(trimmed, ~start=3, ~end=String.length(trimmed))
137
+ } else {
138
+ trimmed
139
+ }
140
+ let withLoc = switch String.lastIndexOf(body, "(") {
141
+ | -1 => body
142
+ | i =>
143
+ let rest = String.slice(body, ~start=i + 1, ~end=String.length(body))
144
+ switch String.lastIndexOf(rest, ")") {
145
+ | -1 => rest
146
+ | j => String.slice(rest, ~start=0, ~end=j)
147
+ }
148
+ }
149
+ let parts = withLoc->String.split(":")
150
+ let n = parts->Array.length
151
+ if n < 3 {
152
+ None
153
+ } else {
154
+ let col = parts->Array.getUnsafe(n - 1)->Int.fromString
155
+ let line = parts->Array.getUnsafe(n - 2)->Int.fromString
156
+ let file =
157
+ parts->Array.slice(~start=0, ~end=n - 2)->Array.join(":")
158
+ let file = if String.startsWith(file, "file://") {
159
+ String.slice(file, ~start=7, ~end=String.length(file))
160
+ } else {
161
+ file
162
+ }
163
+ // Strip the Loader's `?t=N` cache-busting query so locations point at the
164
+ // real file path.
165
+ let file = switch String.indexOf(file, "?") {
166
+ | -1 => file
167
+ | q => String.slice(file, ~start=0, ~end=q)
168
+ }
169
+ switch (line, col) {
170
+ | (Some(l), Some(c)) => Some({file, line: l, column: c})
171
+ | _ => None
172
+ }
173
+ }
174
+ }
175
+
176
+ // Walk a captured stack and return the first frame that lives in a test
177
+ // file outside the reventless-gwt package itself.
178
+ let captureLocation = (skip: int): option<location> => {
179
+ let stack: option<string> = %raw(`(new Error()).stack`)
180
+ switch stack {
181
+ | None => None
182
+ | Some(s) =>
183
+ let lines = s->String.split("\n")
184
+ let total = lines->Array.length
185
+ // Skip the "Error" header, plus `skip` internal frames.
186
+ let rec find = i =>
187
+ if i >= total {
188
+ None
189
+ } else {
190
+ let frame = lines->Array.getUnsafe(i)
191
+ switch parseFrame(frame) {
192
+ | Some(loc)
193
+ if !(loc.file->String.includes("reventless-gwt/src/")) &&
194
+ !(loc.file->String.includes("node_modules/")) =>
195
+ Some(loc)
196
+ | _ => find(i + 1)
197
+ }
198
+ }
199
+ find(skip + 1)
200
+ }
201
+ }
@@ -0,0 +1,215 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stdlib_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
4
+ import * as Outcome$ReventlessGwt from "./Outcome.res.mjs";
5
+
6
+ let active = {
7
+ contents: false
8
+ };
9
+
10
+ let describeStack = {
11
+ contents: []
12
+ };
13
+
14
+ let entries = {
15
+ contents: []
16
+ };
17
+
18
+ let hasOnly = {
19
+ contents: false
20
+ };
21
+
22
+ let skipNext = {
23
+ contents: false
24
+ };
25
+
26
+ let onlyNext = {
27
+ contents: false
28
+ };
29
+
30
+ let skipDepth = {
31
+ contents: 0
32
+ };
33
+
34
+ let currentFile = {
35
+ contents: undefined
36
+ };
37
+
38
+ function activate() {
39
+ active.contents = true;
40
+ describeStack.contents = [];
41
+ entries.contents = [];
42
+ hasOnly.contents = false;
43
+ skipNext.contents = false;
44
+ onlyNext.contents = false;
45
+ currentFile.contents = undefined;
46
+ }
47
+
48
+ function deactivate() {
49
+ active.contents = false;
50
+ }
51
+
52
+ function isActive() {
53
+ return active.contents;
54
+ }
55
+
56
+ function setCurrentFile(path) {
57
+ currentFile.contents = path;
58
+ }
59
+
60
+ function getCurrentFile() {
61
+ return currentFile.contents;
62
+ }
63
+
64
+ function markSkipNext() {
65
+ skipNext.contents = true;
66
+ }
67
+
68
+ function markOnlyNext() {
69
+ onlyNext.contents = true;
70
+ hasOnly.contents = true;
71
+ }
72
+
73
+ function pushDescribe(label, body) {
74
+ describeStack.contents = describeStack.contents.concat([label]);
75
+ body();
76
+ let ds = describeStack.contents;
77
+ describeStack.contents = ds.slice(0, ds.length - 1 | 0);
78
+ }
79
+
80
+ function buildId(describePath, name) {
81
+ let prefix = describePath.join("::");
82
+ if (prefix === "") {
83
+ return name;
84
+ } else {
85
+ return prefix + "::" + name;
86
+ }
87
+ }
88
+
89
+ function push(slice, location, name, body) {
90
+ let status = skipDepth.contents > 0 ? "Skipped" : (
91
+ skipNext.contents ? (skipNext.contents = false, "Skipped") : (
92
+ onlyNext.contents ? (onlyNext.contents = false, "Only") : "Runnable"
93
+ )
94
+ );
95
+ let describePath = describeStack.contents;
96
+ let id = buildId(describePath, name);
97
+ let entry = {
98
+ id: id,
99
+ name: name,
100
+ describePath: describePath,
101
+ slice: slice,
102
+ body: body,
103
+ status: status,
104
+ location: location
105
+ };
106
+ entries.contents = entries.contents.concat([entry]);
107
+ }
108
+
109
+ function pushTodo(name) {
110
+ let describePath = describeStack.contents;
111
+ let id = buildId(describePath, name);
112
+ let entry_body = () => Promise.resolve(Outcome$ReventlessGwt.pass);
113
+ let entry = {
114
+ id: id,
115
+ name: name,
116
+ describePath: describePath,
117
+ slice: undefined,
118
+ body: entry_body,
119
+ status: "Skipped",
120
+ location: undefined
121
+ };
122
+ entries.contents = entries.contents.concat([entry]);
123
+ }
124
+
125
+ function drain() {
126
+ let result = entries.contents;
127
+ entries.contents = [];
128
+ describeStack.contents = [];
129
+ return result;
130
+ }
131
+
132
+ function parseFrame(frame) {
133
+ let trimmed = frame.trim();
134
+ let body = trimmed.startsWith("at ") ? trimmed.slice(3, trimmed.length) : trimmed;
135
+ let i = body.lastIndexOf("(");
136
+ let withLoc;
137
+ if (i !== -1) {
138
+ let rest = body.slice(i + 1 | 0, body.length);
139
+ let j = rest.lastIndexOf(")");
140
+ withLoc = j !== -1 ? rest.slice(0, j) : rest;
141
+ } else {
142
+ withLoc = body;
143
+ }
144
+ let parts = withLoc.split(":");
145
+ let n = parts.length;
146
+ if (n < 3) {
147
+ return;
148
+ }
149
+ let col = Stdlib_Int.fromString(parts[n - 1 | 0], undefined);
150
+ let line = Stdlib_Int.fromString(parts[n - 2 | 0], undefined);
151
+ let file = parts.slice(0, n - 2 | 0).join(":");
152
+ let file$1 = file.startsWith("file://") ? file.slice(7, file.length) : file;
153
+ let q = file$1.indexOf("?");
154
+ let file$2 = q !== -1 ? file$1.slice(0, q) : file$1;
155
+ if (line !== undefined && col !== undefined) {
156
+ return {
157
+ file: file$2,
158
+ line: line,
159
+ column: col
160
+ };
161
+ }
162
+ }
163
+
164
+ function captureLocation(skip) {
165
+ let stack = ((new Error()).stack);
166
+ if (stack === undefined) {
167
+ return;
168
+ }
169
+ let lines = stack.split("\n");
170
+ let total = lines.length;
171
+ let _i = skip + 1 | 0;
172
+ while (true) {
173
+ let i = _i;
174
+ if (i >= total) {
175
+ return;
176
+ }
177
+ let frame = lines[i];
178
+ let loc = parseFrame(frame);
179
+ if (loc !== undefined) {
180
+ if (!loc.file.includes("reventless-gwt/src/") && !loc.file.includes("node_modules/")) {
181
+ return loc;
182
+ }
183
+ _i = i + 1 | 0;
184
+ continue;
185
+ }
186
+ _i = i + 1 | 0;
187
+ continue;
188
+ };
189
+ }
190
+
191
+ export {
192
+ active,
193
+ describeStack,
194
+ entries,
195
+ hasOnly,
196
+ skipNext,
197
+ onlyNext,
198
+ skipDepth,
199
+ currentFile,
200
+ activate,
201
+ deactivate,
202
+ isActive,
203
+ setCurrentFile,
204
+ getCurrentFile,
205
+ markSkipNext,
206
+ markOnlyNext,
207
+ pushDescribe,
208
+ buildId,
209
+ push,
210
+ pushTodo,
211
+ drain,
212
+ parseFrame,
213
+ captureLocation,
214
+ }
215
+ /* No side effect */
@@ -0,0 +1,104 @@
1
+ // Pure helpers mapping a file path to its Reventless component {kind, name}
2
+ // using the folder-name convention (the same vocabulary the plugin generator
3
+ // classifies by). Shared by the discovery `item` annotation (FormatterVsCode)
4
+ // and the src/ component inventory (ComponentScan). No I/O — path strings only.
5
+
6
+ type component = {kind: string, name: string}
7
+
8
+ // Folder names that denote a component kind. A file counts as a component when
9
+ // its immediate parent folder is one of these.
10
+ let kindFolders = [
11
+ "Aggregate",
12
+ "StateChangeSlice",
13
+ "StateViewSlice",
14
+ "StateViewSliceStream",
15
+ "ReadModel",
16
+ "ReadModelStream",
17
+ "AutomationSlice",
18
+ "InboundTranslationSlice",
19
+ "OutboundTranslationSlice",
20
+ "Extension",
21
+ "ExtensionPoint",
22
+ "Task",
23
+ ]
24
+
25
+ let isKindFolder = (segment: string) => kindFolders->Array.includes(segment)
26
+
27
+ // Body-file suffixes stripped to recover the spec stem (longest first so
28
+ // `_ExtensionPointMapping` is removed before `_ExtensionPoint`). Spec and body
29
+ // files in one folder collapse to the same component name.
30
+ let bodySuffixes = [
31
+ "_ExtensionPointMapping",
32
+ "_ExtensionPoint",
33
+ "_Extension",
34
+ "_Projections",
35
+ "_Projection",
36
+ "_Mappings",
37
+ "_Behavior",
38
+ "_Automation",
39
+ "_Translation",
40
+ ]
41
+
42
+ let stripBody = (stem: string) =>
43
+ switch bodySuffixes->Array.find(suf => String.endsWith(stem, suf)) {
44
+ | Some(suf) => String.slice(stem, ~start=0, ~end=String.length(stem) - String.length(suf))
45
+ | None => stem
46
+ }
47
+
48
+ // Strip the GWT test marker (`_GWT` / `GwtTest` / `Gwt`) — the three shapes
49
+ // `Discovery.isGwtTestFile` recognises.
50
+ let stripGwt = (stem: string) =>
51
+ if String.endsWith(stem, "_GWT") {
52
+ String.slice(stem, ~start=0, ~end=String.length(stem) - 4)
53
+ } else if String.endsWith(stem, "GwtTest") {
54
+ String.slice(stem, ~start=0, ~end=String.length(stem) - 7)
55
+ } else if String.endsWith(stem, "Gwt") {
56
+ String.slice(stem, ~start=0, ~end=String.length(stem) - 3)
57
+ } else {
58
+ stem
59
+ }
60
+
61
+ // Last path segment (POSIX paths — the monorepo's tooling normalises to `/`).
62
+ let basename = (path: string) =>
63
+ switch path->String.split("/")->Array.last {
64
+ | Some(s) => s
65
+ | None => path
66
+ }
67
+
68
+ // Parent folder name (the segment before the filename), if any.
69
+ let parentFolder = (path: string) => {
70
+ let segs = path->String.split("/")
71
+ let n = segs->Array.length
72
+ n >= 2 ? segs->Array.get(n - 2) : None
73
+ }
74
+
75
+ // Strip the compiled / source extension from a filename.
76
+ let stem = (filename: string) =>
77
+ if String.endsWith(filename, ".res.mjs") {
78
+ String.slice(filename, ~start=0, ~end=String.length(filename) - 8)
79
+ } else if String.endsWith(filename, ".res.js") {
80
+ String.slice(filename, ~start=0, ~end=String.length(filename) - 7)
81
+ } else if String.endsWith(filename, ".resi") {
82
+ String.slice(filename, ~start=0, ~end=String.length(filename) - 5)
83
+ } else if String.endsWith(filename, ".res") {
84
+ String.slice(filename, ~start=0, ~end=String.length(filename) - 4)
85
+ } else {
86
+ filename
87
+ }
88
+
89
+ // {kind, name} for a discovered GWT test file, or None if it isn't inside a
90
+ // recognised kind folder.
91
+ let componentOfTestFile = (path: string): option<component> =>
92
+ switch parentFolder(path) {
93
+ | Some(folder) if isKindFolder(folder) =>
94
+ Some({kind: folder, name: path->basename->stem->stripGwt->stripBody})
95
+ | _ => None
96
+ }
97
+
98
+ // {kind, name} for a src/ component file (spec or body), or None.
99
+ let componentOfSrcFile = (path: string): option<component> =>
100
+ switch parentFolder(path) {
101
+ | Some(folder) if isKindFolder(folder) =>
102
+ Some({kind: folder, name: path->basename->stem->stripBody})
103
+ | _ => None
104
+ }
@@ -0,0 +1,120 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
4
+
5
+ let kindFolders = [
6
+ "Aggregate",
7
+ "StateChangeSlice",
8
+ "StateViewSlice",
9
+ "StateViewSliceStream",
10
+ "ReadModel",
11
+ "ReadModelStream",
12
+ "AutomationSlice",
13
+ "InboundTranslationSlice",
14
+ "OutboundTranslationSlice",
15
+ "Extension",
16
+ "ExtensionPoint",
17
+ "Task"
18
+ ];
19
+
20
+ function isKindFolder(segment) {
21
+ return kindFolders.includes(segment);
22
+ }
23
+
24
+ let bodySuffixes = [
25
+ "_ExtensionPointMapping",
26
+ "_ExtensionPoint",
27
+ "_Extension",
28
+ "_Projections",
29
+ "_Projection",
30
+ "_Mappings",
31
+ "_Behavior",
32
+ "_Automation",
33
+ "_Translation"
34
+ ];
35
+
36
+ function stripBody(stem) {
37
+ let suf = bodySuffixes.find(suf => stem.endsWith(suf));
38
+ if (suf !== undefined) {
39
+ return stem.slice(0, stem.length - suf.length | 0);
40
+ } else {
41
+ return stem;
42
+ }
43
+ }
44
+
45
+ function stripGwt(stem) {
46
+ if (stem.endsWith("_GWT")) {
47
+ return stem.slice(0, stem.length - 4 | 0);
48
+ } else if (stem.endsWith("GwtTest")) {
49
+ return stem.slice(0, stem.length - 7 | 0);
50
+ } else if (stem.endsWith("Gwt")) {
51
+ return stem.slice(0, stem.length - 3 | 0);
52
+ } else {
53
+ return stem;
54
+ }
55
+ }
56
+
57
+ function basename(path) {
58
+ let s = Stdlib_Array.last(path.split("/"));
59
+ if (s !== undefined) {
60
+ return s;
61
+ } else {
62
+ return path;
63
+ }
64
+ }
65
+
66
+ function parentFolder(path) {
67
+ let segs = path.split("/");
68
+ let n = segs.length;
69
+ if (n >= 2) {
70
+ return segs[n - 2 | 0];
71
+ }
72
+ }
73
+
74
+ function stem(filename) {
75
+ if (filename.endsWith(".res.mjs")) {
76
+ return filename.slice(0, filename.length - 8 | 0);
77
+ } else if (filename.endsWith(".res.js")) {
78
+ return filename.slice(0, filename.length - 7 | 0);
79
+ } else if (filename.endsWith(".resi")) {
80
+ return filename.slice(0, filename.length - 5 | 0);
81
+ } else if (filename.endsWith(".res")) {
82
+ return filename.slice(0, filename.length - 4 | 0);
83
+ } else {
84
+ return filename;
85
+ }
86
+ }
87
+
88
+ function componentOfTestFile(path) {
89
+ let folder = parentFolder(path);
90
+ if (folder !== undefined && kindFolders.includes(folder)) {
91
+ return {
92
+ kind: folder,
93
+ name: stripBody(stripGwt(stem(basename(path))))
94
+ };
95
+ }
96
+ }
97
+
98
+ function componentOfSrcFile(path) {
99
+ let folder = parentFolder(path);
100
+ if (folder !== undefined && kindFolders.includes(folder)) {
101
+ return {
102
+ kind: folder,
103
+ name: stripBody(stem(basename(path)))
104
+ };
105
+ }
106
+ }
107
+
108
+ export {
109
+ kindFolders,
110
+ isKindFolder,
111
+ bodySuffixes,
112
+ stripBody,
113
+ stripGwt,
114
+ basename,
115
+ parentFolder,
116
+ stem,
117
+ componentOfTestFile,
118
+ componentOfSrcFile,
119
+ }
120
+ /* No side effect */