@reventlessdev/reventless-gwt 1.0.0-alpha.100

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 (184) hide show
  1. package/CHANGELOG.md +916 -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 +569 -0
  10. package/src/Behavior_GWT.res.mjs +523 -0
  11. package/src/Bind.res +29 -0
  12. package/src/Bind.res.mjs +22 -0
  13. package/src/BuildClassifier.res +147 -0
  14. package/src/BuildClassifier.res.mjs +136 -0
  15. package/src/Cancellation.res +37 -0
  16. package/src/Cancellation.res.mjs +46 -0
  17. package/src/ChildProcess.res +102 -0
  18. package/src/ChildProcess.res.mjs +107 -0
  19. package/src/Cli.res +905 -0
  20. package/src/Cli.res.mjs +952 -0
  21. package/src/Collector.res +212 -0
  22. package/src/Collector.res.mjs +218 -0
  23. package/src/ComponentMeta.res +77 -0
  24. package/src/ComponentMeta.res.mjs +91 -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 +99 -0
  33. package/src/DomainDeadCode.res +98 -0
  34. package/src/DomainDeadCode.res.mjs +72 -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 +45 -0
  40. package/src/Filter.res.mjs +47 -0
  41. package/src/Flow_GWT.res +664 -0
  42. package/src/Flow_GWT.res.mjs +604 -0
  43. package/src/FormatterHuman.res +109 -0
  44. package/src/FormatterHuman.res.mjs +115 -0
  45. package/src/FormatterJson.res +264 -0
  46. package/src/FormatterJson.res.mjs +322 -0
  47. package/src/FormatterJunit.res +93 -0
  48. package/src/FormatterJunit.res.mjs +90 -0
  49. package/src/FormatterTap.res +100 -0
  50. package/src/FormatterTap.res.mjs +131 -0
  51. package/src/FormatterVsCode.res +352 -0
  52. package/src/FormatterVsCode.res.mjs +546 -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 +119 -0
  62. package/src/LocalHost.res.mjs +99 -0
  63. package/src/Mapping_GWT.res +474 -0
  64. package/src/Mapping_GWT.res.mjs +322 -0
  65. package/src/MismatchRender.res +72 -0
  66. package/src/MismatchRender.res.mjs +127 -0
  67. package/src/MultiSourceProjection_GWT.res +316 -0
  68. package/src/MultiSourceProjection_GWT.res.mjs +354 -0
  69. package/src/OutboundTranslation_GWT.res +238 -0
  70. package/src/OutboundTranslation_GWT.res.mjs +186 -0
  71. package/src/Outcome.res +97 -0
  72. package/src/Outcome.res.mjs +93 -0
  73. package/src/PackageScan.res +80 -0
  74. package/src/PackageScan.res.mjs +81 -0
  75. package/src/PlatformRunner.res +181 -0
  76. package/src/PlatformRunner.res.mjs +176 -0
  77. package/src/PlatformScan.res +142 -0
  78. package/src/PlatformScan.res.mjs +132 -0
  79. package/src/ProcessManager.res +135 -0
  80. package/src/ProcessManager.res.mjs +119 -0
  81. package/src/Projection_GWT.res +312 -0
  82. package/src/Projection_GWT.res.mjs +332 -0
  83. package/src/Query_GWT.res +379 -0
  84. package/src/Query_GWT.res.mjs +367 -0
  85. package/src/RenderRescript.res +125 -0
  86. package/src/RenderRescript.res.mjs +141 -0
  87. package/src/RunWorker.res +24 -0
  88. package/src/RunWorker.res.mjs +16 -0
  89. package/src/RunnerTypes.res +68 -0
  90. package/src/RunnerTypes.res.mjs +60 -0
  91. package/src/SideEffect_GWT.res +157 -0
  92. package/src/SideEffect_GWT.res.mjs +113 -0
  93. package/src/StateChange/ExternalAddCategorySlice.res +25 -0
  94. package/src/StateChange/ExternalAddCategorySlice.res.mjs +34 -0
  95. package/src/StateChange/ExternalAddCategorySlice_Behavior.res +19 -0
  96. package/src/StateChange/ExternalAddCategorySlice_Behavior.res.mjs +46 -0
  97. package/src/StateChange/WithFixtures.res +22 -0
  98. package/src/StateChange/WithFixtures.res.mjs +31 -0
  99. package/src/StateChange/WithFixtures_Behavior.res +17 -0
  100. package/src/StateChange/WithFixtures_Behavior.res.mjs +37 -0
  101. package/src/StateChange/WithManualOpen.res +23 -0
  102. package/src/StateChange/WithManualOpen.res.mjs +31 -0
  103. package/src/StateChange/WithManualOpen_Behavior.res +17 -0
  104. package/src/StateChange/WithManualOpen_Behavior.res.mjs +37 -0
  105. package/src/StubRuntime.res +54 -0
  106. package/src/StubRuntime.res.mjs +55 -0
  107. package/src/TestFixtures.res +16 -0
  108. package/src/TestFixtures.res.mjs +34 -0
  109. package/src/Watch.res +108 -0
  110. package/src/Watch.res.mjs +85 -0
  111. package/src/WatcherProbe.res +56 -0
  112. package/src/WatcherProbe.res.mjs +59 -0
  113. package/src/Worker.res +24 -0
  114. package/src/Worker.res.mjs +9 -0
  115. package/tests/AutomationGwtTest.res +71 -0
  116. package/tests/AutomationGwtTest.res.mjs +189 -0
  117. package/tests/BuildClassifierTest.res +133 -0
  118. package/tests/BuildClassifierTest.res.mjs +176 -0
  119. package/tests/CliRunEntryTest.res +57 -0
  120. package/tests/CliRunEntryTest.res.mjs +65 -0
  121. package/tests/CliWatchScopeTest.res +62 -0
  122. package/tests/CliWatchScopeTest.res.mjs +95 -0
  123. package/tests/ComponentMetaTest.res +67 -0
  124. package/tests/ComponentMetaTest.res.mjs +71 -0
  125. package/tests/DelegateGwtTest.res +487 -0
  126. package/tests/DelegateGwtTest.res.mjs +891 -0
  127. package/tests/DiscoveryTest.res +42 -0
  128. package/tests/DiscoveryTest.res.mjs +43 -0
  129. package/tests/DomainDeadCodeTest.res +119 -0
  130. package/tests/DomainDeadCodeTest.res.mjs +158 -0
  131. package/tests/DomainGraphTest.res +448 -0
  132. package/tests/DomainGraphTest.res.mjs +505 -0
  133. package/tests/FlowAggregateGwtTest.res +171 -0
  134. package/tests/FlowAggregateGwtTest.res.mjs +273 -0
  135. package/tests/FlowCrossPluginGwtTest.res +340 -0
  136. package/tests/FlowCrossPluginGwtTest.res.mjs +798 -0
  137. package/tests/FlowGwtTest.res +222 -0
  138. package/tests/FlowGwtTest.res.mjs +433 -0
  139. package/tests/FormatterGoldenTest.res +125 -0
  140. package/tests/FormatterGoldenTest.res.mjs +162 -0
  141. package/tests/FormatterVsCodeMessageTest.res +41 -0
  142. package/tests/FormatterVsCodeMessageTest.res.mjs +42 -0
  143. package/tests/InboundTranslationGwtTest.res +34 -0
  144. package/tests/InboundTranslationGwtTest.res.mjs +99 -0
  145. package/tests/LocalHostIntegration.res +70 -0
  146. package/tests/LocalHostIntegration.res.mjs +64 -0
  147. package/tests/LocalHostTest.res +71 -0
  148. package/tests/LocalHostTest.res.mjs +63 -0
  149. package/tests/MappingGwtTest.res +350 -0
  150. package/tests/MappingGwtTest.res.mjs +596 -0
  151. package/tests/OutboundTranslationGwtTest.res +59 -0
  152. package/tests/OutboundTranslationGwtTest.res.mjs +139 -0
  153. package/tests/PlatformRunnerTest.res +66 -0
  154. package/tests/PlatformRunnerTest.res.mjs +85 -0
  155. package/tests/PlatformScanTest.res +61 -0
  156. package/tests/PlatformScanTest.res.mjs +56 -0
  157. package/tests/QueryGwtTest.res +161 -0
  158. package/tests/QueryGwtTest.res.mjs +314 -0
  159. package/tests/QueryResolverGwtTest.res +71 -0
  160. package/tests/QueryResolverGwtTest.res.mjs +136 -0
  161. package/tests/RunnerUnitTest.res +219 -0
  162. package/tests/RunnerUnitTest.res.mjs +244 -0
  163. package/tests/SideEffect/FakeOrderNotification.res +23 -0
  164. package/tests/SideEffect/FakeOrderNotification.res.mjs +36 -0
  165. package/tests/SideEffect/FakeOrderNotificationGwtTest.res +35 -0
  166. package/tests/SideEffect/FakeOrderNotificationGwtTest.res.mjs +73 -0
  167. package/tests/SideEffect/MockEmail.res +18 -0
  168. package/tests/SideEffect/MockEmail.res.mjs +47 -0
  169. package/tests/StateChange/ExternalAddCategorySlice_GWT.res +27 -0
  170. package/tests/StateChange/ExternalAddCategorySlice_GWT.res.mjs +70 -0
  171. package/tests/StateChange/WithFixtures_Fixtures.res +9 -0
  172. package/tests/StateChange/WithFixtures_Fixtures.res.mjs +20 -0
  173. package/tests/StateChange/WithFixtures_GWT.res +23 -0
  174. package/tests/StateChange/WithFixtures_GWT.res.mjs +56 -0
  175. package/tests/StateChange/WithManualOpen_Fixtures.res +7 -0
  176. package/tests/StateChange/WithManualOpen_Fixtures.res.mjs +20 -0
  177. package/tests/StateChange/WithManualOpen_GWT.res +17 -0
  178. package/tests/StateChange/WithManualOpen_GWT.res.mjs +56 -0
  179. package/tests/StateChangeSliceGwtTest.res +160 -0
  180. package/tests/StateChangeSliceGwtTest.res.mjs +280 -0
  181. package/tests/StateViewSliceGwtTest.res +64 -0
  182. package/tests/StateViewSliceGwtTest.res.mjs +161 -0
  183. package/tests/WatchDebounceTest.res +106 -0
  184. package/tests/WatchDebounceTest.res.mjs +128 -0
@@ -0,0 +1,212 @@
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
+ // Per-test deadline in ms (from `~timeout` on `testPromise`). `None` = use the
29
+ // runner default. The CLI runner races the body against this; the Jest arm
30
+ // passes it to Jest's own timeout instead.
31
+ timeout: option<int>,
32
+ }
33
+
34
+ let active = ref(false)
35
+ let describeStack: ref<array<string>> = ref([])
36
+ let entries: ref<array<entry>> = ref([])
37
+ let hasOnly = ref(false)
38
+ let skipNext = ref(false)
39
+ let onlyNext = ref(false)
40
+ let skipDepth = ref(0)
41
+ let currentFile: ref<option<string>> = ref(None)
42
+
43
+ let activate = () => {
44
+ active := true
45
+ describeStack := []
46
+ entries := []
47
+ hasOnly := false
48
+ skipNext := false
49
+ onlyNext := false
50
+ // Reset the skip nesting depth too: a throwing `xdescribe` body in a prior
51
+ // file could otherwise leave it > 0, silently skipping every test in every
52
+ // subsequently loaded file.
53
+ skipDepth := 0
54
+ currentFile := None
55
+ }
56
+
57
+ let deactivate = () => active := false
58
+ let isActive = () => active.contents
59
+
60
+ let setCurrentFile = path => currentFile := Some(path)
61
+ let getCurrentFile = () => currentFile.contents
62
+
63
+ let markSkipNext = () => skipNext := true
64
+ let markOnlyNext = () => {
65
+ onlyNext := true
66
+ hasOnly := true
67
+ }
68
+
69
+ let pushDescribe = (label: string, body: unit => unit) => {
70
+ describeStack := Array.concat(describeStack.contents, [label])
71
+ body()
72
+ let ds = describeStack.contents
73
+ describeStack := ds->Array.slice(~start=0, ~end=ds->Array.length - 1)
74
+ }
75
+
76
+ let buildId = (describePath, name) => {
77
+ let prefix = describePath->Array.join("::")
78
+ prefix == "" ? name : prefix ++ "::" ++ name
79
+ }
80
+
81
+ let push = (
82
+ ~slice=?,
83
+ ~location=?,
84
+ ~timeout=?,
85
+ name: string,
86
+ body: unit => promise<Outcome.outcome>,
87
+ ) => {
88
+ let status = if skipDepth.contents > 0 {
89
+ Skipped
90
+ } else if skipNext.contents {
91
+ skipNext := false
92
+ Skipped
93
+ } else if onlyNext.contents {
94
+ onlyNext := false
95
+ Only
96
+ } else {
97
+ Runnable
98
+ }
99
+ let describePath = describeStack.contents
100
+ let id = buildId(describePath, name)
101
+ let entry = {
102
+ id,
103
+ name,
104
+ describePath,
105
+ slice,
106
+ body,
107
+ status,
108
+ location,
109
+ timeout,
110
+ }
111
+ entries.contents->Array.push(entry)
112
+ }
113
+
114
+ // A `todo` placeholder: a spec-less slice still ships a discoverable, compiling
115
+ // `_GWT.res`. Register it as a Skipped entry with a no-op passing body so the
116
+ // CLI runner lists it without executing or failing.
117
+ let pushTodo = (name: string) => {
118
+ let describePath = describeStack.contents
119
+ let id = buildId(describePath, name)
120
+ let entry = {
121
+ id,
122
+ name,
123
+ describePath,
124
+ slice: None,
125
+ body: () => Promise.resolve(Outcome.pass),
126
+ status: Skipped,
127
+ location: None,
128
+ timeout: None,
129
+ }
130
+ entries.contents->Array.push(entry)
131
+ }
132
+
133
+ let drain = () => {
134
+ let result = entries.contents
135
+ entries := []
136
+ describeStack := []
137
+ result
138
+ }
139
+
140
+ // Parse a single V8 stack frame line like:
141
+ // " at body (/abs/path/File.res.mjs:42:13)"
142
+ // " at /abs/path/File.res.mjs:42:13"
143
+ // Returns (file, line, column) if the frame belongs to a non-internal user file.
144
+ let parseFrame = (frame: string): option<location> => {
145
+ let trimmed = frame->String.trim
146
+ let body = if String.startsWith(trimmed, "at ") {
147
+ String.slice(trimmed, ~start=3, ~end=String.length(trimmed))
148
+ } else {
149
+ trimmed
150
+ }
151
+ let withLoc = switch String.lastIndexOf(body, "(") {
152
+ | -1 => body
153
+ | i =>
154
+ let rest = String.slice(body, ~start=i + 1, ~end=String.length(body))
155
+ switch String.lastIndexOf(rest, ")") {
156
+ | -1 => rest
157
+ | j => String.slice(rest, ~start=0, ~end=j)
158
+ }
159
+ }
160
+ let parts = withLoc->String.split(":")
161
+ let n = parts->Array.length
162
+ if n < 3 {
163
+ None
164
+ } else {
165
+ let col = parts->Array.getUnsafe(n - 1)->Int.fromString
166
+ let line = parts->Array.getUnsafe(n - 2)->Int.fromString
167
+ let file =
168
+ parts->Array.slice(~start=0, ~end=n - 2)->Array.join(":")
169
+ let file = if String.startsWith(file, "file://") {
170
+ String.slice(file, ~start=7, ~end=String.length(file))
171
+ } else {
172
+ file
173
+ }
174
+ // Strip the Loader's `?t=N` cache-busting query so locations point at the
175
+ // real file path.
176
+ let file = switch String.indexOf(file, "?") {
177
+ | -1 => file
178
+ | q => String.slice(file, ~start=0, ~end=q)
179
+ }
180
+ switch (line, col) {
181
+ | (Some(l), Some(c)) => Some({file, line: l, column: c})
182
+ | _ => None
183
+ }
184
+ }
185
+ }
186
+
187
+ // Walk a captured stack and return the first frame that lives in a test
188
+ // file outside the reventless-gwt package itself.
189
+ let captureLocation = (skip: int): option<location> => {
190
+ let stack: option<string> = %raw(`(new Error()).stack`)
191
+ switch stack {
192
+ | None => None
193
+ | Some(s) =>
194
+ let lines = s->String.split("\n")
195
+ let total = lines->Array.length
196
+ // Skip the "Error" header, plus `skip` internal frames.
197
+ let rec find = i =>
198
+ if i >= total {
199
+ None
200
+ } else {
201
+ let frame = lines->Array.getUnsafe(i)
202
+ switch parseFrame(frame) {
203
+ | Some(loc)
204
+ if !(loc.file->String.includes("reventless-gwt/src/")) &&
205
+ !(loc.file->String.includes("node_modules/")) =>
206
+ Some(loc)
207
+ | _ => find(i + 1)
208
+ }
209
+ }
210
+ find(skip + 1)
211
+ }
212
+ }
@@ -0,0 +1,218 @@
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
+ skipDepth.contents = 0;
46
+ currentFile.contents = undefined;
47
+ }
48
+
49
+ function deactivate() {
50
+ active.contents = false;
51
+ }
52
+
53
+ function isActive() {
54
+ return active.contents;
55
+ }
56
+
57
+ function setCurrentFile(path) {
58
+ currentFile.contents = path;
59
+ }
60
+
61
+ function getCurrentFile() {
62
+ return currentFile.contents;
63
+ }
64
+
65
+ function markSkipNext() {
66
+ skipNext.contents = true;
67
+ }
68
+
69
+ function markOnlyNext() {
70
+ onlyNext.contents = true;
71
+ hasOnly.contents = true;
72
+ }
73
+
74
+ function pushDescribe(label, body) {
75
+ describeStack.contents = describeStack.contents.concat([label]);
76
+ body();
77
+ let ds = describeStack.contents;
78
+ describeStack.contents = ds.slice(0, ds.length - 1 | 0);
79
+ }
80
+
81
+ function buildId(describePath, name) {
82
+ let prefix = describePath.join("::");
83
+ if (prefix === "") {
84
+ return name;
85
+ } else {
86
+ return prefix + "::" + name;
87
+ }
88
+ }
89
+
90
+ function push(slice, location, timeout, name, body) {
91
+ let status = skipDepth.contents > 0 ? "Skipped" : (
92
+ skipNext.contents ? (skipNext.contents = false, "Skipped") : (
93
+ onlyNext.contents ? (onlyNext.contents = false, "Only") : "Runnable"
94
+ )
95
+ );
96
+ let describePath = describeStack.contents;
97
+ let id = buildId(describePath, name);
98
+ let entry = {
99
+ id: id,
100
+ name: name,
101
+ describePath: describePath,
102
+ slice: slice,
103
+ body: body,
104
+ status: status,
105
+ location: location,
106
+ timeout: timeout
107
+ };
108
+ entries.contents.push(entry);
109
+ }
110
+
111
+ function pushTodo(name) {
112
+ let describePath = describeStack.contents;
113
+ let id = buildId(describePath, name);
114
+ let entry_body = () => Promise.resolve(Outcome$ReventlessGwt.pass);
115
+ let entry = {
116
+ id: id,
117
+ name: name,
118
+ describePath: describePath,
119
+ slice: undefined,
120
+ body: entry_body,
121
+ status: "Skipped",
122
+ location: undefined,
123
+ timeout: undefined
124
+ };
125
+ entries.contents.push(entry);
126
+ }
127
+
128
+ function drain() {
129
+ let result = entries.contents;
130
+ entries.contents = [];
131
+ describeStack.contents = [];
132
+ return result;
133
+ }
134
+
135
+ function parseFrame(frame) {
136
+ let trimmed = frame.trim();
137
+ let body = trimmed.startsWith("at ") ? trimmed.slice(3, trimmed.length) : trimmed;
138
+ let i = body.lastIndexOf("(");
139
+ let withLoc;
140
+ if (i !== -1) {
141
+ let rest = body.slice(i + 1 | 0, body.length);
142
+ let j = rest.lastIndexOf(")");
143
+ withLoc = j !== -1 ? rest.slice(0, j) : rest;
144
+ } else {
145
+ withLoc = body;
146
+ }
147
+ let parts = withLoc.split(":");
148
+ let n = parts.length;
149
+ if (n < 3) {
150
+ return;
151
+ }
152
+ let col = Stdlib_Int.fromString(parts[n - 1 | 0], undefined);
153
+ let line = Stdlib_Int.fromString(parts[n - 2 | 0], undefined);
154
+ let file = parts.slice(0, n - 2 | 0).join(":");
155
+ let file$1 = file.startsWith("file://") ? file.slice(7, file.length) : file;
156
+ let q = file$1.indexOf("?");
157
+ let file$2 = q !== -1 ? file$1.slice(0, q) : file$1;
158
+ if (line !== undefined && col !== undefined) {
159
+ return {
160
+ file: file$2,
161
+ line: line,
162
+ column: col
163
+ };
164
+ }
165
+ }
166
+
167
+ function captureLocation(skip) {
168
+ let stack = ((new Error()).stack);
169
+ if (stack === undefined) {
170
+ return;
171
+ }
172
+ let lines = stack.split("\n");
173
+ let total = lines.length;
174
+ let _i = skip + 1 | 0;
175
+ while (true) {
176
+ let i = _i;
177
+ if (i >= total) {
178
+ return;
179
+ }
180
+ let frame = lines[i];
181
+ let loc = parseFrame(frame);
182
+ if (loc !== undefined) {
183
+ if (!loc.file.includes("reventless-gwt/src/") && !loc.file.includes("node_modules/")) {
184
+ return loc;
185
+ }
186
+ _i = i + 1 | 0;
187
+ continue;
188
+ }
189
+ _i = i + 1 | 0;
190
+ continue;
191
+ };
192
+ }
193
+
194
+ export {
195
+ active,
196
+ describeStack,
197
+ entries,
198
+ hasOnly,
199
+ skipNext,
200
+ onlyNext,
201
+ skipDepth,
202
+ currentFile,
203
+ activate,
204
+ deactivate,
205
+ isActive,
206
+ setCurrentFile,
207
+ getCurrentFile,
208
+ markSkipNext,
209
+ markOnlyNext,
210
+ pushDescribe,
211
+ buildId,
212
+ push,
213
+ pushTodo,
214
+ drain,
215
+ parseFrame,
216
+ captureLocation,
217
+ }
218
+ /* No side effect */
@@ -0,0 +1,77 @@
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
+ // The folder→kind vocabulary and body-file suffixes are the single source in
9
+ // `Reventless.ComponentKind` (shared with the plugin generator). Deriving from it
10
+ // means the gwt discovery recognises exactly the folder spellings the generator
11
+ // classifies — including the plural / short forms this file used to miss.
12
+ module Kind = Reventless.ComponentKind
13
+
14
+ // Strip a body-file suffix (longest first) to recover the spec stem, so spec and
15
+ // body files in one folder collapse to the same component name.
16
+ let stripBody = (stem: string) =>
17
+ switch Kind.bodySuffixes->Array.find(suf => String.endsWith(stem, suf)) {
18
+ | Some(suf) => String.slice(stem, ~start=0, ~end=String.length(stem) - String.length(suf))
19
+ | None => stem
20
+ }
21
+
22
+ // Strip the GWT test marker (`_GWT` / `GwtTest` / `Gwt`) — the three shapes
23
+ // `Discovery.isGwtTestFile` recognises.
24
+ let stripGwt = (stem: string) =>
25
+ if String.endsWith(stem, "_GWT") {
26
+ String.slice(stem, ~start=0, ~end=String.length(stem) - 4)
27
+ } else if String.endsWith(stem, "GwtTest") {
28
+ String.slice(stem, ~start=0, ~end=String.length(stem) - 7)
29
+ } else if String.endsWith(stem, "Gwt") {
30
+ String.slice(stem, ~start=0, ~end=String.length(stem) - 3)
31
+ } else {
32
+ stem
33
+ }
34
+
35
+ // Last path segment (POSIX paths — the monorepo's tooling normalises to `/`).
36
+ let basename = (path: string) =>
37
+ switch path->String.split("/")->Array.last {
38
+ | Some(s) => s
39
+ | None => path
40
+ }
41
+
42
+ // Parent folder name (the segment before the filename), if any.
43
+ let parentFolder = (path: string) => {
44
+ let segs = path->String.split("/")
45
+ let n = segs->Array.length
46
+ n >= 2 ? segs->Array.get(n - 2) : None
47
+ }
48
+
49
+ // Strip the compiled / source extension from a filename.
50
+ let stem = (filename: string) =>
51
+ if String.endsWith(filename, ".res.mjs") {
52
+ String.slice(filename, ~start=0, ~end=String.length(filename) - 8)
53
+ } else if String.endsWith(filename, ".res.js") {
54
+ String.slice(filename, ~start=0, ~end=String.length(filename) - 7)
55
+ } else if String.endsWith(filename, ".resi") {
56
+ String.slice(filename, ~start=0, ~end=String.length(filename) - 5)
57
+ } else if String.endsWith(filename, ".res") {
58
+ String.slice(filename, ~start=0, ~end=String.length(filename) - 4)
59
+ } else {
60
+ filename
61
+ }
62
+
63
+ // {kind, name} for a discovered GWT test file, or None if it isn't inside a
64
+ // recognised kind folder. `kind` is the canonical folder name, so a plural
65
+ // folder (`Aggregates/`) reports the same kind as its singular form.
66
+ let componentOfTestFile = (path: string): option<component> =>
67
+ switch parentFolder(path)->Option.flatMap(Kind.folderToKind) {
68
+ | Some(kind) => Some({kind: Kind.folderName(kind), name: path->basename->stem->stripGwt->stripBody})
69
+ | None => None
70
+ }
71
+
72
+ // {kind, name} for a src/ component file (spec or body), or None.
73
+ let componentOfSrcFile = (path: string): option<component> =>
74
+ switch parentFolder(path)->Option.flatMap(Kind.folderToKind) {
75
+ | Some(kind) => Some({kind: Kind.folderName(kind), name: path->basename->stem->stripBody})
76
+ | None => None
77
+ }
@@ -0,0 +1,91 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
4
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
5
+ import * as ComponentKind$Reventless from "@reventlessdev/reventless-spec/src/components/ComponentKind.res.mjs";
6
+
7
+ function stripBody(stem) {
8
+ let suf = ComponentKind$Reventless.bodySuffixes.find(suf => stem.endsWith(suf));
9
+ if (suf !== undefined) {
10
+ return stem.slice(0, stem.length - suf.length | 0);
11
+ } else {
12
+ return stem;
13
+ }
14
+ }
15
+
16
+ function stripGwt(stem) {
17
+ if (stem.endsWith("_GWT")) {
18
+ return stem.slice(0, stem.length - 4 | 0);
19
+ } else if (stem.endsWith("GwtTest")) {
20
+ return stem.slice(0, stem.length - 7 | 0);
21
+ } else if (stem.endsWith("Gwt")) {
22
+ return stem.slice(0, stem.length - 3 | 0);
23
+ } else {
24
+ return stem;
25
+ }
26
+ }
27
+
28
+ function basename(path) {
29
+ let s = Stdlib_Array.last(path.split("/"));
30
+ if (s !== undefined) {
31
+ return s;
32
+ } else {
33
+ return path;
34
+ }
35
+ }
36
+
37
+ function parentFolder(path) {
38
+ let segs = path.split("/");
39
+ let n = segs.length;
40
+ if (n >= 2) {
41
+ return segs[n - 2 | 0];
42
+ }
43
+ }
44
+
45
+ function stem(filename) {
46
+ if (filename.endsWith(".res.mjs")) {
47
+ return filename.slice(0, filename.length - 8 | 0);
48
+ } else if (filename.endsWith(".res.js")) {
49
+ return filename.slice(0, filename.length - 7 | 0);
50
+ } else if (filename.endsWith(".resi")) {
51
+ return filename.slice(0, filename.length - 5 | 0);
52
+ } else if (filename.endsWith(".res")) {
53
+ return filename.slice(0, filename.length - 4 | 0);
54
+ } else {
55
+ return filename;
56
+ }
57
+ }
58
+
59
+ function componentOfTestFile(path) {
60
+ let kind = Stdlib_Option.flatMap(parentFolder(path), ComponentKind$Reventless.folderToKind);
61
+ if (kind !== undefined) {
62
+ return {
63
+ kind: ComponentKind$Reventless.folderName(kind),
64
+ name: stripBody(stripGwt(stem(basename(path))))
65
+ };
66
+ }
67
+ }
68
+
69
+ function componentOfSrcFile(path) {
70
+ let kind = Stdlib_Option.flatMap(parentFolder(path), ComponentKind$Reventless.folderToKind);
71
+ if (kind !== undefined) {
72
+ return {
73
+ kind: ComponentKind$Reventless.folderName(kind),
74
+ name: stripBody(stem(basename(path)))
75
+ };
76
+ }
77
+ }
78
+
79
+ let Kind;
80
+
81
+ export {
82
+ Kind,
83
+ stripBody,
84
+ stripGwt,
85
+ basename,
86
+ parentFolder,
87
+ stem,
88
+ componentOfTestFile,
89
+ componentOfSrcFile,
90
+ }
91
+ /* No side effect */
@@ -0,0 +1,103 @@
1
+ // Walks each owning package's src/ tree and enumerates its Reventless
2
+ // components by folder convention — including components with no GWT tests.
3
+ // This is the inventory the `components` NDJSON event carries: the basis for
4
+ // the activity-bar Plugin→kind→component tree and "untested slice" coverage.
5
+ // Computed fresh per discovery so it never drifts as components are added.
6
+
7
+ type component = {
8
+ // Absolute directory of the owning package (matches the `dir` in `packages`).
9
+ dir: string,
10
+ kind: string,
11
+ name: string,
12
+ // Absolute paths of the component's `src/` files (spec + body files like
13
+ // `*_Behavior.res` / `*_Mappings.res`), in walk order. Lets the client list
14
+ // spec / implementation files under each component node.
15
+ files: array<string>,
16
+ }
17
+
18
+ type dirent = {
19
+ name: string,
20
+ @as("isDirectory") _isDirectory: unit => bool,
21
+ @as("isFile") _isFile: unit => bool,
22
+ }
23
+ type readdirOpts = {withFileTypes: bool}
24
+ @module("node:fs/promises")
25
+ external _readdir: (string, readdirOpts) => promise<array<dirent>> = "readdir"
26
+ @module("node:fs") external _existsSync: string => bool = "existsSync"
27
+ @module("node:path") external join: (string, string) => string = "join"
28
+
29
+ let ignoreNames = ["node_modules", ".git", "dist", "lib", ".history"]
30
+ let shouldIgnore = (name: string) => Array.includes(ignoreNames, name)
31
+
32
+ // A directory carrying this sentinel file — and its whole subtree — is pruned
33
+ // from the component scan, matching `Discovery`'s `.gwtignore` convention so
34
+ // the domain tree and the test tree exclude the same generated/vendored trees.
35
+ let gwtIgnoreFile = ".gwtignore"
36
+ let isPruned = (entries: array<dirent>) => entries->Array.some(e => e.name == gwtIgnoreFile)
37
+
38
+ // Source `.res` only — `.res.mjs` / `.res.js` end in `.mjs` / `.js`, and `.resi`
39
+ // in `.resi`, so a plain `.res` suffix test excludes compiled output + interfaces.
40
+ let isSrcResFile = (name: string) => String.endsWith(name, ".res")
41
+
42
+ let rec walk = async (dir: string, acc: array<string>): array<string> => {
43
+ let entries = try {
44
+ await _readdir(dir, {withFileTypes: true})
45
+ } catch {
46
+ | _ => []
47
+ }
48
+ if isPruned(entries) {
49
+ acc
50
+ } else {
51
+ let found = ref(acc)
52
+ for i in 0 to entries->Array.length - 1 {
53
+ let entry = entries->Array.getUnsafe(i)
54
+ if !shouldIgnore(entry.name) {
55
+ let full = join(dir, entry.name)
56
+ if entry._isDirectory() {
57
+ let nested = await walk(full, [])
58
+ found := Array.concat(found.contents, nested)
59
+ } else if entry._isFile() && isSrcResFile(entry.name) {
60
+ found := Array.concat(found.contents, [full])
61
+ }
62
+ }
63
+ }
64
+ found.contents
65
+ }
66
+ }
67
+
68
+ // Enumerate components across the given package directories (deduplicated per
69
+ // dir+kind+name, declaration order preserved). Each component accumulates the
70
+ // source files that map to it (spec + body files).
71
+ type orderEntry = {dir: string, kind: string, name: string, key: string}
72
+
73
+ let scan = async (pkgDirs: array<string>): array<component> => {
74
+ let order = []
75
+ let filesByKey = Dict.make()
76
+ for i in 0 to pkgDirs->Array.length - 1 {
77
+ let dir = pkgDirs->Array.getUnsafe(i)
78
+ let srcDir = join(dir, "src")
79
+ if _existsSync(srcDir) {
80
+ let files = await walk(srcDir, [])
81
+ files->Array.forEach(file =>
82
+ switch ComponentMeta.componentOfSrcFile(file) {
83
+ | Some(c) =>
84
+ let key = dir ++ "::" ++ c.kind ++ "::" ++ c.name
85
+ switch filesByKey->Dict.get(key) {
86
+ | Some(existing) => filesByKey->Dict.set(key, Array.concat(existing, [file]))
87
+ | None => {
88
+ filesByKey->Dict.set(key, [file])
89
+ order->Array.push({dir, kind: c.kind, name: c.name, key})
90
+ }
91
+ }
92
+ | None => ()
93
+ }
94
+ )
95
+ }
96
+ }
97
+ order->Array.map(o => {
98
+ dir: o.dir,
99
+ kind: o.kind,
100
+ name: o.name,
101
+ files: filesByKey->Dict.get(o.key)->Option.getOr([]),
102
+ })
103
+ }