@reventlessdev/reventless-gwt 1.0.0-alpha.115 → 1.0.0-alpha.116

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 (91) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/package.json +5 -12
  3. package/rescript.json +0 -1
  4. package/bin/reventless-dev.mjs +0 -21
  5. package/src/Bind.res +0 -29
  6. package/src/Bind.res.mjs +0 -22
  7. package/src/BuildClassifier.res +0 -147
  8. package/src/BuildClassifier.res.mjs +0 -136
  9. package/src/Cancellation.res +0 -37
  10. package/src/Cancellation.res.mjs +0 -46
  11. package/src/ChildProcess.res +0 -102
  12. package/src/ChildProcess.res.mjs +0 -107
  13. package/src/Cli.res +0 -889
  14. package/src/Cli.res.mjs +0 -944
  15. package/src/Collector.res +0 -222
  16. package/src/Collector.res.mjs +0 -228
  17. package/src/ComponentMeta.res +0 -77
  18. package/src/ComponentMeta.res.mjs +0 -91
  19. package/src/ComponentScan.res +0 -101
  20. package/src/ComponentScan.res.mjs +0 -92
  21. package/src/Discovery.res +0 -102
  22. package/src/Discovery.res.mjs +0 -86
  23. package/src/DomainDeadCode.res +0 -98
  24. package/src/DomainDeadCode.res.mjs +0 -72
  25. package/src/DomainGraph.res +0 -0
  26. package/src/DomainGraph.res.mjs +0 -0
  27. package/src/ExnMessage.res +0 -14
  28. package/src/ExnMessage.res.mjs +0 -17
  29. package/src/ExnReflect.mjs +0 -24
  30. package/src/Filter.res +0 -45
  31. package/src/Filter.res.mjs +0 -47
  32. package/src/FormatterHuman.res +0 -109
  33. package/src/FormatterHuman.res.mjs +0 -115
  34. package/src/FormatterJson.res +0 -264
  35. package/src/FormatterJson.res.mjs +0 -322
  36. package/src/FormatterJunit.res +0 -93
  37. package/src/FormatterJunit.res.mjs +0 -90
  38. package/src/FormatterTap.res +0 -100
  39. package/src/FormatterTap.res.mjs +0 -131
  40. package/src/FormatterVsCode.res +0 -352
  41. package/src/FormatterVsCode.res.mjs +0 -546
  42. package/src/Loader.res +0 -24
  43. package/src/Loader.res.mjs +0 -23
  44. package/src/PackageScan.res +0 -80
  45. package/src/PackageScan.res.mjs +0 -81
  46. package/src/PlatformRunner.res +0 -191
  47. package/src/PlatformRunner.res.mjs +0 -178
  48. package/src/PlatformScan.res +0 -141
  49. package/src/PlatformScan.res.mjs +0 -124
  50. package/src/ProcessManager.res +0 -135
  51. package/src/ProcessManager.res.mjs +0 -119
  52. package/src/RunWorker.res +0 -24
  53. package/src/RunWorker.res.mjs +0 -16
  54. package/src/RunnerTypes.res +0 -68
  55. package/src/RunnerTypes.res.mjs +0 -60
  56. package/src/ScanIgnore.res +0 -11
  57. package/src/ScanIgnore.res.mjs +0 -23
  58. package/src/Watch.res +0 -102
  59. package/src/Watch.res.mjs +0 -80
  60. package/src/WatcherProbe.res +0 -56
  61. package/src/WatcherProbe.res.mjs +0 -59
  62. package/src/Worker.res +0 -24
  63. package/src/Worker.res.mjs +0 -9
  64. package/tests/BuildClassifierTest.res +0 -133
  65. package/tests/BuildClassifierTest.res.mjs +0 -176
  66. package/tests/CliRunEntryTest.res +0 -57
  67. package/tests/CliRunEntryTest.res.mjs +0 -65
  68. package/tests/CliWatchScopeTest.res +0 -62
  69. package/tests/CliWatchScopeTest.res.mjs +0 -95
  70. package/tests/ComponentMetaTest.res +0 -67
  71. package/tests/ComponentMetaTest.res.mjs +0 -71
  72. package/tests/DiscoveryTest.res +0 -42
  73. package/tests/DiscoveryTest.res.mjs +0 -43
  74. package/tests/DomainDeadCodeTest.res +0 -119
  75. package/tests/DomainDeadCodeTest.res.mjs +0 -158
  76. package/tests/DomainGraphTest.res +0 -448
  77. package/tests/DomainGraphTest.res.mjs +0 -505
  78. package/tests/ExnMessageTest.res +0 -42
  79. package/tests/ExnMessageTest.res.mjs +0 -64
  80. package/tests/FormatterGoldenTest.res +0 -125
  81. package/tests/FormatterGoldenTest.res.mjs +0 -162
  82. package/tests/FormatterVsCodeMessageTest.res +0 -41
  83. package/tests/FormatterVsCodeMessageTest.res.mjs +0 -42
  84. package/tests/PlatformRunnerTest.res +0 -66
  85. package/tests/PlatformRunnerTest.res.mjs +0 -85
  86. package/tests/PlatformScanTest.res +0 -61
  87. package/tests/PlatformScanTest.res.mjs +0 -56
  88. package/tests/RunnerUnitTest.res +0 -219
  89. package/tests/RunnerUnitTest.res.mjs +0 -244
  90. package/tests/WatchDebounceTest.res +0 -106
  91. package/tests/WatchDebounceTest.res.mjs +0 -128
@@ -1,101 +0,0 @@
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
-
30
- // A directory carrying this sentinel file — and its whole subtree — is pruned
31
- // from the component scan, matching `Discovery`'s `.gwtignore` convention so
32
- // the domain tree and the test tree exclude the same generated/vendored trees.
33
- let gwtIgnoreFile = ".gwtignore"
34
- let isPruned = (entries: array<dirent>) => entries->Array.some(e => e.name == gwtIgnoreFile)
35
-
36
- // Source `.res` only — `.res.mjs` / `.res.js` end in `.mjs` / `.js`, and `.resi`
37
- // in `.resi`, so a plain `.res` suffix test excludes compiled output + interfaces.
38
- let isSrcResFile = (name: string) => String.endsWith(name, ".res")
39
-
40
- let rec walk = async (dir: string, acc: array<string>): array<string> => {
41
- let entries = try {
42
- await _readdir(dir, {withFileTypes: true})
43
- } catch {
44
- | _ => []
45
- }
46
- if isPruned(entries) {
47
- acc
48
- } else {
49
- let found = ref(acc)
50
- for i in 0 to entries->Array.length - 1 {
51
- let entry = entries->Array.getUnsafe(i)
52
- if !ScanIgnore.shouldIgnore(entry.name) {
53
- let full = join(dir, entry.name)
54
- if entry._isDirectory() {
55
- let nested = await walk(full, [])
56
- found := Array.concat(found.contents, nested)
57
- } else if entry._isFile() && isSrcResFile(entry.name) {
58
- found := Array.concat(found.contents, [full])
59
- }
60
- }
61
- }
62
- found.contents
63
- }
64
- }
65
-
66
- // Enumerate components across the given package directories (deduplicated per
67
- // dir+kind+name, declaration order preserved). Each component accumulates the
68
- // source files that map to it (spec + body files).
69
- type orderEntry = {dir: string, kind: string, name: string, key: string}
70
-
71
- let scan = async (pkgDirs: array<string>): array<component> => {
72
- let order = []
73
- let filesByKey = Dict.make()
74
- for i in 0 to pkgDirs->Array.length - 1 {
75
- let dir = pkgDirs->Array.getUnsafe(i)
76
- let srcDir = join(dir, "src")
77
- if _existsSync(srcDir) {
78
- let files = await walk(srcDir, [])
79
- files->Array.forEach(file =>
80
- switch ComponentMeta.componentOfSrcFile(file) {
81
- | Some(c) =>
82
- let key = dir ++ "::" ++ c.kind ++ "::" ++ c.name
83
- switch filesByKey->Dict.get(key) {
84
- | Some(existing) => filesByKey->Dict.set(key, Array.concat(existing, [file]))
85
- | None => {
86
- filesByKey->Dict.set(key, [file])
87
- order->Array.push({dir, kind: c.kind, name: c.name, key})
88
- }
89
- }
90
- | None => ()
91
- }
92
- )
93
- }
94
- }
95
- order->Array.map(o => {
96
- dir: o.dir,
97
- kind: o.kind,
98
- name: o.name,
99
- files: filesByKey->Dict.get(o.key)->Option.getOr([]),
100
- })
101
- }
@@ -1,92 +0,0 @@
1
- // Generated by ReScript, PLEASE EDIT WITH CARE
2
-
3
- import * as Nodefs from "node:fs";
4
- import * as Nodepath from "node:path";
5
- import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
6
- import * as Promises from "node:fs/promises";
7
- import * as ScanIgnore$ReventlessGwt from "./ScanIgnore.res.mjs";
8
- import * as ComponentMeta$ReventlessGwt from "./ComponentMeta.res.mjs";
9
-
10
- let gwtIgnoreFile = ".gwtignore";
11
-
12
- function isPruned(entries) {
13
- return entries.some(e => e.name === gwtIgnoreFile);
14
- }
15
-
16
- function isSrcResFile(name) {
17
- return name.endsWith(".res");
18
- }
19
-
20
- async function walk(dir, acc) {
21
- let entries;
22
- try {
23
- entries = await Promises.readdir(dir, {
24
- withFileTypes: true
25
- });
26
- } catch (exn) {
27
- entries = [];
28
- }
29
- if (isPruned(entries)) {
30
- return acc;
31
- }
32
- let found = acc;
33
- for (let i = 0, i_finish = entries.length; i < i_finish; ++i) {
34
- let entry = entries[i];
35
- if (!ScanIgnore$ReventlessGwt.shouldIgnore(entry.name)) {
36
- let full = Nodepath.join(dir, entry.name);
37
- if (entry.isDirectory()) {
38
- let nested = await walk(full, []);
39
- found = found.concat(nested);
40
- } else if (entry.isFile() && entry.name.endsWith(".res")) {
41
- found = found.concat([full]);
42
- }
43
- }
44
- }
45
- return found;
46
- }
47
-
48
- async function scan(pkgDirs) {
49
- let order = [];
50
- let filesByKey = {};
51
- for (let i = 0, i_finish = pkgDirs.length; i < i_finish; ++i) {
52
- let dir = pkgDirs[i];
53
- let srcDir = Nodepath.join(dir, "src");
54
- if (Nodefs.existsSync(srcDir)) {
55
- let files = await walk(srcDir, []);
56
- files.forEach(file => {
57
- let c = ComponentMeta$ReventlessGwt.componentOfSrcFile(file);
58
- if (c === undefined) {
59
- return;
60
- }
61
- let key = dir + "::" + c.kind + "::" + c.name;
62
- let existing = filesByKey[key];
63
- if (existing !== undefined) {
64
- filesByKey[key] = existing.concat([file]);
65
- } else {
66
- filesByKey[key] = [file];
67
- order.push({
68
- dir: dir,
69
- kind: c.kind,
70
- name: c.name,
71
- key: key
72
- });
73
- }
74
- });
75
- }
76
- }
77
- return order.map(o => ({
78
- dir: o.dir,
79
- kind: o.kind,
80
- name: o.name,
81
- files: Stdlib_Option.getOr(filesByKey[o.key], [])
82
- }));
83
- }
84
-
85
- export {
86
- gwtIgnoreFile,
87
- isPruned,
88
- isSrcResFile,
89
- walk,
90
- scan,
91
- }
92
- /* node:fs Not a pure module */
package/src/Discovery.res DELETED
@@ -1,102 +0,0 @@
1
- // Walks a directory tree and returns absolute paths of compiled GWT test
2
- // files (`*_GWT.res.mjs` plus `*GwtTest.res.mjs` — the two shapes used in
3
- // the monorepo today). Respects a minimal ignore set (`node_modules`,
4
- // `lib`, `.git`, `dist`, `.history`). Pruning `lib` covers compiled
5
- // `lib/bs` / `lib/ocaml` outputs. Full `.gitignore` parsing is out of scope;
6
- // the ignore set is enough to make a bare cwd scan (the auto-discovery
7
- // default) cheap and correct across a multi-package workspace.
8
-
9
- type dirent = {
10
- name: string,
11
- @as("isDirectory") _isDirectory: unit => bool,
12
- @as("isFile") _isFile: unit => bool,
13
- }
14
-
15
- type readdirOpts = {withFileTypes: bool}
16
-
17
- @module("node:fs/promises")
18
- external _readdir: (string, readdirOpts) => promise<array<dirent>> = "readdir"
19
-
20
- type stats = {
21
- @as("isDirectory") _isDirectory: unit => bool,
22
- @as("isFile") _isFile: unit => bool,
23
- }
24
-
25
- @module("node:fs/promises") external _stat: string => promise<stats> = "stat"
26
-
27
- @module("node:path") external join: (string, string) => string = "join"
28
- @module("node:path") external isAbsolute: string => bool = "isAbsolute"
29
- @module("node:path") external resolve: string => string = "resolve"
30
-
31
- // A directory carrying this sentinel file — and its whole subtree — is pruned
32
- // from discovery. Unlike the `ScanIgnore.names` dir-name list, this is filesystem
33
- // state, so it excludes generated/vendored trees (e.g. codegen golden fixtures)
34
- // even when compiled `*_GWT.res.mjs` are present on disk.
35
- let gwtIgnoreFile = ".gwtignore"
36
- let isPruned = (entries: array<dirent>) => entries->Array.some(e => e.name == gwtIgnoreFile)
37
-
38
- let isGwtTestFile = (name: string) =>
39
- String.endsWith(name, "_GWT.res.mjs") ||
40
- String.endsWith(name, "GwtTest.res.mjs") ||
41
- String.endsWith(name, "Gwt.res.mjs")
42
-
43
- // Depth-first, pushing into a shared accumulator instead of rebuilding it with
44
- // `Array.concat` per entry (which was O(n²) over a large tree). The traversal
45
- // order is unchanged: files and nested subtree results still land in on-disk
46
- // entry order.
47
- let rec walk = async (dir: string, acc: array<string>): unit => {
48
- let entries = try {
49
- await _readdir(dir, {withFileTypes: true})
50
- } catch {
51
- | _ => []
52
- }
53
- if isPruned(entries) {
54
- ()
55
- } else {
56
- for i in 0 to entries->Array.length - 1 {
57
- let entry = entries->Array.getUnsafe(i)
58
- if !ScanIgnore.shouldIgnore(entry.name) {
59
- let full = join(dir, entry.name)
60
- if entry._isDirectory() {
61
- await walk(full, acc)
62
- } else if entry._isFile() && isGwtTestFile(entry.name) {
63
- acc->Array.push(full)
64
- }
65
- }
66
- }
67
- }
68
- }
69
-
70
- // Returns absolute paths of `*GWT*.res.mjs` test files reachable from the
71
- // supplied roots. A root may be a directory or a single file.
72
- let discover = async (roots: array<string>): array<string> => {
73
- let found = []
74
- for i in 0 to roots->Array.length - 1 {
75
- let root = roots->Array.getUnsafe(i)
76
- let absolute = isAbsolute(root) ? root : resolve(root)
77
- let isDir = try {
78
- let s = await _stat(absolute)
79
- s._isDirectory()
80
- } catch {
81
- | _ => false
82
- }
83
- if isDir {
84
- await walk(absolute, found)
85
- } else if isGwtTestFile(absolute) {
86
- found->Array.push(absolute)
87
- }
88
- }
89
- // Deduplicate (if roots overlap).
90
- let seen = Dict.make()
91
- let unique = []
92
- found->Array.forEach(path =>
93
- switch seen->Dict.get(path) {
94
- | Some(_) => ()
95
- | None => {
96
- seen->Dict.set(path, true)
97
- unique->Array.push(path)
98
- }
99
- }
100
- )
101
- unique
102
- }
@@ -1,86 +0,0 @@
1
- // Generated by ReScript, PLEASE EDIT WITH CARE
2
-
3
- import * as Nodepath from "node:path";
4
- import * as Promises from "node:fs/promises";
5
- import * as ScanIgnore$ReventlessGwt from "./ScanIgnore.res.mjs";
6
-
7
- let gwtIgnoreFile = ".gwtignore";
8
-
9
- function isPruned(entries) {
10
- return entries.some(e => e.name === gwtIgnoreFile);
11
- }
12
-
13
- function isGwtTestFile(name) {
14
- if (name.endsWith("_GWT.res.mjs") || name.endsWith("GwtTest.res.mjs")) {
15
- return true;
16
- } else {
17
- return name.endsWith("Gwt.res.mjs");
18
- }
19
- }
20
-
21
- async function walk(dir, acc) {
22
- let entries;
23
- try {
24
- entries = await Promises.readdir(dir, {
25
- withFileTypes: true
26
- });
27
- } catch (exn) {
28
- entries = [];
29
- }
30
- if (isPruned(entries)) {
31
- return;
32
- }
33
- for (let i = 0, i_finish = entries.length; i < i_finish; ++i) {
34
- let entry = entries[i];
35
- if (!ScanIgnore$ReventlessGwt.shouldIgnore(entry.name)) {
36
- let full = Nodepath.join(dir, entry.name);
37
- if (entry.isDirectory()) {
38
- await walk(full, acc);
39
- } else if (entry.isFile() && isGwtTestFile(entry.name)) {
40
- acc.push(full);
41
- }
42
- }
43
- }
44
- }
45
-
46
- async function discover(roots) {
47
- let found = [];
48
- for (let i = 0, i_finish = roots.length; i < i_finish; ++i) {
49
- let root = roots[i];
50
- let absolute = Nodepath.isAbsolute(root) ? root : Nodepath.resolve(root);
51
- let isDir;
52
- try {
53
- let s = await Promises.stat(absolute);
54
- isDir = s.isDirectory();
55
- } catch (exn) {
56
- isDir = false;
57
- }
58
- if (isDir) {
59
- await walk(absolute, found);
60
- } else if (isGwtTestFile(absolute)) {
61
- found.push(absolute);
62
- }
63
- }
64
- let seen = {};
65
- let unique = [];
66
- found.forEach(path => {
67
- let match = seen[path];
68
- if (match !== undefined) {
69
- return;
70
- } else {
71
- seen[path] = true;
72
- unique.push(path);
73
- return;
74
- }
75
- });
76
- return unique;
77
- }
78
-
79
- export {
80
- gwtIgnoreFile,
81
- isPruned,
82
- isGwtTestFile,
83
- walk,
84
- discover,
85
- }
86
- /* node:path Not a pure module */
@@ -1,98 +0,0 @@
1
- // Domain-level dead-code analysis (Phase 5). Pure reflection over the loaded
2
- // pluginStructures (LocalHost.loadGraph): finds produced event
3
- // types that no component consumes — the event-sourcing counterpart to reactive
4
- // dead code, the domain layer the language LSP cannot see.
5
- //
6
- // An event is reachable when ANY of these hold:
7
- // 1. Some component lists it in `consumedEventTypes` — the DCB-style link used by
8
- // state-view slices, automation slices, outbound translations, and aggregate /
9
- // state-change-slice consistency reads. Scanned across ALL plugins, so a
10
- // cross-plugin consumer clears it.
11
- // 2. The producing write-side has a non-empty `linkedViews`. Classic aggregate →
12
- // read-model projections express the link on the PRODUCER side (the read model's
13
- // `consumedEventTypes` is empty; it projects the aggregate's whole stream). This
14
- // is event-opaque, so a write-side with at least one linked view is treated as
15
- // fully consumed — see the precision caveat below.
16
- // A produced event matched by neither of these is reported as an orphan, anchored on
17
- // the producing write-side declaration.
18
- //
19
- // Precision caveat: because classic read models carry no per-event consumption, rule
20
- // (2) is conservative — a write-side with a linked view is never flagged, even if a
21
- // specific event it produces is projected by nothing. This avoids false positives at
22
- // the cost of missing per-event orphans inside an otherwise-projected aggregate. DCB
23
- // consumers (rule 1) keep full per-event precision.
24
- //
25
- // Scope (v1): orphan produced events only. "Extension points with zero extensions"
26
- // and "read models nothing resolves against" need data `pluginStructure` does not
27
- // carry (the EP declaration list / the id-resolver table) — deferred.
28
-
29
- // Finding kinds are the typed protocol vocabulary — constructing the variant here
30
- // (not a string literal) means a typo or rename is a compile error, and the emitter
31
- // forwards findings without conversion.
32
- module P = ReventlessDomainProtocol.Protocol
33
-
34
- type finding = {
35
- kind: P.deadCodeKind,
36
- pluginName: string, // plugin owning the producer
37
- componentName: string, // the producing aggregate / state-change slice
38
- detail: string, // the orphaned event type
39
- }
40
-
41
- let analyze = (
42
- ~structures: array<(string, Reventless.Plugin.pluginStructure)>,
43
- ): array<finding> => {
44
- // Every event type consumed anywhere becomes a key in `consumed`.
45
- let consumed = Dict.make()
46
- let mark = (evt: string) => consumed->Dict.set(evt, true)
47
- structures->Array.forEach(((_, s)) => {
48
- s.readModels->Array.forEach((q: Reventless.Plugin.queryableDef) =>
49
- q.consumedEventTypes->Array.forEach(mark)
50
- )
51
- s.stateViewSlices->Array.forEach((q: Reventless.Plugin.queryableDef) =>
52
- q.consumedEventTypes->Array.forEach(mark)
53
- )
54
- s.aggregates->Array.forEach((w: Reventless.Plugin.writableDef) =>
55
- w.consumedEventTypes->Array.forEach(mark)
56
- )
57
- s.stateChangeSlices->Array.forEach((w: Reventless.Plugin.writableDef) =>
58
- w.consumedEventTypes->Array.forEach(mark)
59
- )
60
- s.automationSlices->Array.forEach((a: Reventless.Plugin.automationSliceDef) =>
61
- a.consumedEventTypes->Array.forEach(mark)
62
- )
63
- s.outboundTranslationSlices->Array.forEach((o: Reventless.Plugin.outboundTranslationSliceDef) =>
64
- o.consumedEventTypes->Array.forEach(mark)
65
- )
66
- s.extensions->Array.forEach((e: Reventless.Plugin.extensionDef) =>
67
- e.eventTypes->Array.forEach(mark)
68
- )
69
- })
70
-
71
- // Produced events with no consumer, reported per producing write-side. A write-side
72
- // with a linked view is treated as fully consumed (rule 2 above).
73
- let findings = []
74
- let scanProducers = (pluginName, writables: array<Reventless.Plugin.writableDef>) =>
75
- writables->Array.forEach(w =>
76
- if w.linkedViews->Array.length > 0 {
77
- ()
78
- } else {
79
- w.producedEventTypes->Array.forEach(evt =>
80
- switch consumed->Dict.get(evt) {
81
- | Some(_) => ()
82
- | None =>
83
- findings->Array.push({
84
- kind: OrphanEvent,
85
- pluginName,
86
- componentName: w.name,
87
- detail: evt,
88
- })
89
- }
90
- )
91
- }
92
- )
93
- structures->Array.forEach(((pluginName, s)) => {
94
- scanProducers(pluginName, s.aggregates)
95
- scanProducers(pluginName, s.stateChangeSlices)
96
- })
97
- findings
98
- }
@@ -1,72 +0,0 @@
1
- // Generated by ReScript, PLEASE EDIT WITH CARE
2
-
3
-
4
- function analyze(structures) {
5
- let consumed = {};
6
- let mark = evt => {
7
- consumed[evt] = true;
8
- };
9
- structures.forEach(param => {
10
- let s = param[1];
11
- s.readModels.forEach(q => {
12
- q.consumedEventTypes.forEach(mark);
13
- });
14
- s.stateViewSlices.forEach(q => {
15
- q.consumedEventTypes.forEach(mark);
16
- });
17
- s.aggregates.forEach(w => {
18
- w.consumedEventTypes.forEach(mark);
19
- });
20
- s.stateChangeSlices.forEach(w => {
21
- w.consumedEventTypes.forEach(mark);
22
- });
23
- s.automationSlices.forEach(a => {
24
- a.consumedEventTypes.forEach(mark);
25
- });
26
- s.outboundTranslationSlices.forEach(o => {
27
- o.consumedEventTypes.forEach(mark);
28
- });
29
- s.extensions.forEach(e => {
30
- e.eventTypes.forEach(mark);
31
- });
32
- });
33
- let findings = [];
34
- let scanProducers = (pluginName, writables) => {
35
- writables.forEach(w => {
36
- if (w.linkedViews.length !== 0) {
37
- return;
38
- } else {
39
- w.producedEventTypes.forEach(evt => {
40
- let match = consumed[evt];
41
- if (match !== undefined) {
42
- return;
43
- } else {
44
- findings.push({
45
- kind: "OrphanEvent",
46
- pluginName: pluginName,
47
- componentName: w.name,
48
- detail: evt
49
- });
50
- return;
51
- }
52
- });
53
- return;
54
- }
55
- });
56
- };
57
- structures.forEach(param => {
58
- let s = param[1];
59
- let pluginName = param[0];
60
- scanProducers(pluginName, s.aggregates);
61
- scanProducers(pluginName, s.stateChangeSlices);
62
- });
63
- return findings;
64
- }
65
-
66
- let P;
67
-
68
- export {
69
- P,
70
- analyze,
71
- }
72
- /* No side effect */
Binary file
Binary file
@@ -1,14 +0,0 @@
1
- // Typed bindings for the exception reflection in the companion `ExnReflect.mjs`.
2
- // Inspecting an arbitrary thrown value's shape (`.message`, `RE_EXN_ID`/`_1`,
3
- // `.stack`, a bare string, `throw null`) is untyped work ReScript can't
4
- // pattern-match, so it lives whole in that JS module — no `%raw`, no `Obj.magic`
5
- // here. Used by the runner's catch block to surface a test body's real error
6
- // (e.g. a slice's `failwith("not implemented: …")`) instead of "unknown error".
7
-
8
- // A human-readable message from any thrown value.
9
- @module("./ExnReflect.mjs")
10
- external extract: exn => string = "extractMessage"
11
-
12
- // The `.stack` of a thrown JS `Error`, or "" when absent.
13
- @module("./ExnReflect.mjs")
14
- external stack: exn => string = "stackOf"
@@ -1,17 +0,0 @@
1
- // Generated by ReScript, PLEASE EDIT WITH CARE
2
-
3
- import * as ExnReflectMjs from "./ExnReflect.mjs";
4
-
5
- function extract(prim) {
6
- return ExnReflectMjs.extractMessage(prim);
7
- }
8
-
9
- function stack(prim) {
10
- return ExnReflectMjs.stackOf(prim);
11
- }
12
-
13
- export {
14
- extract,
15
- stack,
16
- }
17
- /* ./ExnReflect.mjs Not a pure module */
@@ -1,24 +0,0 @@
1
- // Untyped reflection over an arbitrary thrown value — the shapes ReScript can't
2
- // pattern-match. Kept whole in JS so the ReScript side (ExnMessage.res) is a
3
- // clean typed binding with no `%raw` or `Obj.magic`.
4
-
5
- // A human-readable message: a `throw null`, a bare thrown string, a JS `Error`
6
- // (`.message`), or a ReScript exception's constructor payload (`RE_EXN_ID` +
7
- // `_1`, e.g. `failwith("…")` raises `Failure`).
8
- export function extractMessage(e) {
9
- if (e == null) return "unknown error";
10
- if (typeof e === "string") return e;
11
- if (typeof e.message === "string" && e.message.length) return e.message;
12
- if (typeof e.RE_EXN_ID === "string") {
13
- const id = e.RE_EXN_ID;
14
- const dot = id.lastIndexOf(".");
15
- const tag = dot >= 0 ? id.slice(dot + 1) : id;
16
- return typeof e._1 === "string" && e._1.length ? e._1 : tag;
17
- }
18
- return "unknown error";
19
- }
20
-
21
- // The `.stack` of a thrown JS `Error`, or "" for anything without one.
22
- export function stackOf(e) {
23
- return (e && e.stack) || "";
24
- }
package/src/Filter.res DELETED
@@ -1,45 +0,0 @@
1
- // Source-level test filters: `only`, `skip`, `xtest`, `xdescribe`.
2
- //
3
- // Jest ships `describe.only` / `test.only` / `xtest` as properties/aliases
4
- // on the globals; reventless-gwt exposes them as explicit helpers. When the
5
- // CLI runner is active, they mutate `Collector` state; when Jest is driving,
6
- // they forward to the Jest globals.
7
-
8
- @val external jestXTest: (string, unit => unit) => unit = "xtest"
9
- @val external jestXDescribe: (string, unit => unit) => unit = "xdescribe"
10
-
11
- // Mark the next registered `test` / `testPromise` as skipped.
12
- let skip = () => Collector.markSkipNext()
13
-
14
- // Mark the next registered `test` / `testPromise` as the focus of the run.
15
- // If at least one entry is flagged `Only`, only those entries execute.
16
- let only = () => Collector.markOnlyNext()
17
-
18
- // xtest: register a test but mark it as skipped.
19
- let xtest = (~slice as _: option<string>=?, name: string, _body: unit => Outcome.outcome) =>
20
- if Collector.isActive() {
21
- Collector.markSkipNext()
22
- Collector.push(name, () => Promise.resolve(Outcome.pass))
23
- } else {
24
- jestXTest(name, () => ())
25
- }
26
-
27
- // xdescribe: register a describe block with every nested test pre-marked
28
- // skipped.
29
- let xdescribe = (label: string, body: unit => unit) =>
30
- if Collector.isActive() {
31
- Collector.skipDepth := Collector.skipDepth.contents + 1
32
- // Decrement on both the normal and the throwing path (ReScript has no
33
- // `finally`): a body that raises must not leak the incremented depth and
34
- // skip every following sibling/file.
35
- try {
36
- Collector.pushDescribe(label, body)
37
- Collector.skipDepth := Collector.skipDepth.contents - 1
38
- } catch {
39
- | e =>
40
- Collector.skipDepth := Collector.skipDepth.contents - 1
41
- throw(e)
42
- }
43
- } else {
44
- jestXDescribe(label, body)
45
- }