@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.
- package/CHANGELOG.md +11 -0
- package/package.json +5 -12
- package/rescript.json +0 -1
- package/bin/reventless-dev.mjs +0 -21
- package/src/Bind.res +0 -29
- package/src/Bind.res.mjs +0 -22
- package/src/BuildClassifier.res +0 -147
- package/src/BuildClassifier.res.mjs +0 -136
- package/src/Cancellation.res +0 -37
- package/src/Cancellation.res.mjs +0 -46
- package/src/ChildProcess.res +0 -102
- package/src/ChildProcess.res.mjs +0 -107
- package/src/Cli.res +0 -889
- package/src/Cli.res.mjs +0 -944
- package/src/Collector.res +0 -222
- package/src/Collector.res.mjs +0 -228
- package/src/ComponentMeta.res +0 -77
- package/src/ComponentMeta.res.mjs +0 -91
- package/src/ComponentScan.res +0 -101
- package/src/ComponentScan.res.mjs +0 -92
- package/src/Discovery.res +0 -102
- package/src/Discovery.res.mjs +0 -86
- package/src/DomainDeadCode.res +0 -98
- package/src/DomainDeadCode.res.mjs +0 -72
- package/src/DomainGraph.res +0 -0
- package/src/DomainGraph.res.mjs +0 -0
- package/src/ExnMessage.res +0 -14
- package/src/ExnMessage.res.mjs +0 -17
- package/src/ExnReflect.mjs +0 -24
- package/src/Filter.res +0 -45
- package/src/Filter.res.mjs +0 -47
- package/src/FormatterHuman.res +0 -109
- package/src/FormatterHuman.res.mjs +0 -115
- package/src/FormatterJson.res +0 -264
- package/src/FormatterJson.res.mjs +0 -322
- package/src/FormatterJunit.res +0 -93
- package/src/FormatterJunit.res.mjs +0 -90
- package/src/FormatterTap.res +0 -100
- package/src/FormatterTap.res.mjs +0 -131
- package/src/FormatterVsCode.res +0 -352
- package/src/FormatterVsCode.res.mjs +0 -546
- package/src/Loader.res +0 -24
- package/src/Loader.res.mjs +0 -23
- package/src/PackageScan.res +0 -80
- package/src/PackageScan.res.mjs +0 -81
- package/src/PlatformRunner.res +0 -191
- package/src/PlatformRunner.res.mjs +0 -178
- package/src/PlatformScan.res +0 -141
- package/src/PlatformScan.res.mjs +0 -124
- package/src/ProcessManager.res +0 -135
- package/src/ProcessManager.res.mjs +0 -119
- package/src/RunWorker.res +0 -24
- package/src/RunWorker.res.mjs +0 -16
- package/src/RunnerTypes.res +0 -68
- package/src/RunnerTypes.res.mjs +0 -60
- package/src/ScanIgnore.res +0 -11
- package/src/ScanIgnore.res.mjs +0 -23
- package/src/Watch.res +0 -102
- package/src/Watch.res.mjs +0 -80
- package/src/WatcherProbe.res +0 -56
- package/src/WatcherProbe.res.mjs +0 -59
- package/src/Worker.res +0 -24
- package/src/Worker.res.mjs +0 -9
- package/tests/BuildClassifierTest.res +0 -133
- package/tests/BuildClassifierTest.res.mjs +0 -176
- package/tests/CliRunEntryTest.res +0 -57
- package/tests/CliRunEntryTest.res.mjs +0 -65
- package/tests/CliWatchScopeTest.res +0 -62
- package/tests/CliWatchScopeTest.res.mjs +0 -95
- package/tests/ComponentMetaTest.res +0 -67
- package/tests/ComponentMetaTest.res.mjs +0 -71
- package/tests/DiscoveryTest.res +0 -42
- package/tests/DiscoveryTest.res.mjs +0 -43
- package/tests/DomainDeadCodeTest.res +0 -119
- package/tests/DomainDeadCodeTest.res.mjs +0 -158
- package/tests/DomainGraphTest.res +0 -448
- package/tests/DomainGraphTest.res.mjs +0 -505
- package/tests/ExnMessageTest.res +0 -42
- package/tests/ExnMessageTest.res.mjs +0 -64
- package/tests/FormatterGoldenTest.res +0 -125
- package/tests/FormatterGoldenTest.res.mjs +0 -162
- package/tests/FormatterVsCodeMessageTest.res +0 -41
- package/tests/FormatterVsCodeMessageTest.res.mjs +0 -42
- package/tests/PlatformRunnerTest.res +0 -66
- package/tests/PlatformRunnerTest.res.mjs +0 -85
- package/tests/PlatformScanTest.res +0 -61
- package/tests/PlatformScanTest.res.mjs +0 -56
- package/tests/RunnerUnitTest.res +0 -219
- package/tests/RunnerUnitTest.res.mjs +0 -244
- package/tests/WatchDebounceTest.res +0 -106
- package/tests/WatchDebounceTest.res.mjs +0 -128
package/src/Watch.res.mjs
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
-
|
|
3
|
-
import * as Chokidar from "chokidar";
|
|
4
|
-
import * as ScanIgnore$ReventlessGwt from "./ScanIgnore.res.mjs";
|
|
5
|
-
|
|
6
|
-
function isStructuralSource(event, path) {
|
|
7
|
-
if (event === "Unlink") {
|
|
8
|
-
return path.endsWith(".res");
|
|
9
|
-
} else {
|
|
10
|
-
return false;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function debounce(wait, fn) {
|
|
15
|
-
let pending = {
|
|
16
|
-
contents: undefined
|
|
17
|
-
};
|
|
18
|
-
let structural = {
|
|
19
|
-
contents: []
|
|
20
|
-
};
|
|
21
|
-
let sawOther = {
|
|
22
|
-
contents: false
|
|
23
|
-
};
|
|
24
|
-
let otherEvent = {
|
|
25
|
-
contents: "Change"
|
|
26
|
-
};
|
|
27
|
-
let otherPath = {
|
|
28
|
-
contents: ""
|
|
29
|
-
};
|
|
30
|
-
return (event, path) => {
|
|
31
|
-
if (isStructuralSource(event, path)) {
|
|
32
|
-
if (!structural.contents.includes(path)) {
|
|
33
|
-
structural.contents = structural.contents.concat([path]);
|
|
34
|
-
}
|
|
35
|
-
} else {
|
|
36
|
-
sawOther.contents = true;
|
|
37
|
-
otherEvent.contents = event;
|
|
38
|
-
otherPath.contents = path;
|
|
39
|
-
}
|
|
40
|
-
let t = pending.contents;
|
|
41
|
-
if (t !== undefined) {
|
|
42
|
-
clearTimeout(t);
|
|
43
|
-
}
|
|
44
|
-
pending.contents = setTimeout(() => {
|
|
45
|
-
pending.contents = undefined;
|
|
46
|
-
let paths = structural.contents;
|
|
47
|
-
let other = sawOther.contents;
|
|
48
|
-
let oe = otherEvent.contents;
|
|
49
|
-
let op = otherPath.contents;
|
|
50
|
-
structural.contents = [];
|
|
51
|
-
sawOther.contents = false;
|
|
52
|
-
otherEvent.contents = "Change";
|
|
53
|
-
otherPath.contents = "";
|
|
54
|
-
if (paths.length !== 0) {
|
|
55
|
-
paths.forEach(p => fn("Unlink", p));
|
|
56
|
-
return;
|
|
57
|
-
} else if (other) {
|
|
58
|
-
return fn(oe, op);
|
|
59
|
-
} else {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
}, wait);
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function start(roots, onChange) {
|
|
67
|
-
let w = Chokidar.watch(roots, {
|
|
68
|
-
ignoreInitial: true,
|
|
69
|
-
ignored: ScanIgnore$ReventlessGwt.globs
|
|
70
|
-
});
|
|
71
|
-
let debounced = debounce(120, onChange);
|
|
72
|
-
return w.on("add", p => debounced("Add", p)).on("change", p => debounced("Change", p)).on("unlink", p => debounced("Unlink", p));
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export {
|
|
76
|
-
isStructuralSource,
|
|
77
|
-
debounce,
|
|
78
|
-
start,
|
|
79
|
-
}
|
|
80
|
-
/* chokidar Not a pure module */
|
package/src/WatcherProbe.res
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
// Detects whether a ReScript build watcher is already running for a package, so
|
|
2
|
-
// the watch manager can defer to a developer's (or rescript-vscode's) own
|
|
3
|
-
// watcher rather than spawn a duplicate. ReScript records the building process's
|
|
4
|
-
// PID in a lock file and liveness-checks it (a dead-pid lock is taken over), so
|
|
5
|
-
// the reliable signal is: lock present AND its pid is alive. A stale lock is
|
|
6
|
-
// harmless — rescript self-heals it on the next build.
|
|
7
|
-
//
|
|
8
|
-
// Two lock files exist depending on which command form started the build:
|
|
9
|
-
// - `lib/watch.lock` — canonical `rescript watch` (used by rescript-vscode
|
|
10
|
-
// ≥ 1.73's "Start Build", and the non-deprecated form)
|
|
11
|
-
// - `lib/rescript.lock` — legacy `rescript build -w` (what `pnpm run start`
|
|
12
|
-
// still maps to in the example packages)
|
|
13
|
-
// Both must be checked: missing `watch.lock` is exactly how the rescript-vscode
|
|
14
|
-
// watcher slipped past an earlier rescript.lock-only probe, causing a doomed
|
|
15
|
-
// duplicate spawn.
|
|
16
|
-
|
|
17
|
-
@module("node:fs") external _existsSync: string => bool = "existsSync"
|
|
18
|
-
@module("node:fs") external _readFileSync: (string, string) => string = "readFileSync"
|
|
19
|
-
@module("node:path") external join: (string, string) => string = "join"
|
|
20
|
-
@val external _processKillSig: (int, int) => unit = "process.kill"
|
|
21
|
-
|
|
22
|
-
// `process.kill(pid, 0)` performs no signal delivery — it only probes existence.
|
|
23
|
-
// It throws `ESRCH` when the pid is gone (dead) and `EPERM` when the pid exists
|
|
24
|
-
// but is owned by another user (alive, just not ours). A bare catch-all treated
|
|
25
|
-
// EPERM as dead, which would spawn a duplicate watcher over a live foreign one;
|
|
26
|
-
// treat EPERM as alive.
|
|
27
|
-
let _errCode: JsExn.t => option<string> = %raw(`(e) => (e && typeof e.code === "string") ? e.code : undefined`)
|
|
28
|
-
let isAlive = (pid: int): bool =>
|
|
29
|
-
try {
|
|
30
|
-
_processKillSig(pid, 0)
|
|
31
|
-
true
|
|
32
|
-
} catch {
|
|
33
|
-
| exn =>
|
|
34
|
-
switch JsExn.fromException(exn) {
|
|
35
|
-
| Some(e) => _errCode(e) == Some("EPERM")
|
|
36
|
-
| None => false
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
let libLock = (dir: string, name: string): string => join(join(dir, "lib"), name)
|
|
41
|
-
|
|
42
|
-
// True when the named lock file exists and holds a live pid.
|
|
43
|
-
let lockHasLivePid = (lock: string): bool =>
|
|
44
|
-
if _existsSync(lock) {
|
|
45
|
-
switch Int.fromString(String.trim(_readFileSync(lock, "utf8"))) {
|
|
46
|
-
| Some(pid) => isAlive(pid)
|
|
47
|
-
| None => false
|
|
48
|
-
}
|
|
49
|
-
} else {
|
|
50
|
-
false
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// True when an external watcher (either command form) already owns this
|
|
54
|
-
// package's build.
|
|
55
|
-
let hasLiveWatcher = (dir: string): bool =>
|
|
56
|
-
lockHasLivePid(libLock(dir, "watch.lock")) || lockHasLivePid(libLock(dir, "rescript.lock"))
|
package/src/WatcherProbe.res.mjs
DELETED
|
@@ -1,59 +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_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
|
|
6
|
-
import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
|
|
7
|
-
import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
|
|
8
|
-
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
9
|
-
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
10
|
-
|
|
11
|
-
let _errCode = ((e) => (e && typeof e.code === "string") ? e.code : undefined);
|
|
12
|
-
|
|
13
|
-
function isAlive(pid) {
|
|
14
|
-
try {
|
|
15
|
-
process.kill(pid, 0);
|
|
16
|
-
return true;
|
|
17
|
-
} catch (raw_exn) {
|
|
18
|
-
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
19
|
-
let e = Stdlib_JsExn.fromException(exn);
|
|
20
|
-
if (e !== undefined) {
|
|
21
|
-
return Primitive_object.equal(_errCode(Primitive_option.valFromOption(e)), "EPERM");
|
|
22
|
-
} else {
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function libLock(dir, name) {
|
|
29
|
-
return Nodepath.join(Nodepath.join(dir, "lib"), name);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function lockHasLivePid(lock) {
|
|
33
|
-
if (!Nodefs.existsSync(lock)) {
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
|
-
let pid = Stdlib_Int.fromString(Nodefs.readFileSync(lock, "utf8").trim(), undefined);
|
|
37
|
-
if (pid !== undefined) {
|
|
38
|
-
return isAlive(pid);
|
|
39
|
-
} else {
|
|
40
|
-
return false;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function hasLiveWatcher(dir) {
|
|
45
|
-
if (lockHasLivePid(libLock(dir, "watch.lock"))) {
|
|
46
|
-
return true;
|
|
47
|
-
} else {
|
|
48
|
-
return lockHasLivePid(libLock(dir, "rescript.lock"));
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export {
|
|
53
|
-
_errCode,
|
|
54
|
-
isAlive,
|
|
55
|
-
libLock,
|
|
56
|
-
lockHasLivePid,
|
|
57
|
-
hasLiveWatcher,
|
|
58
|
-
}
|
|
59
|
-
/* node:fs Not a pure module */
|
package/src/Worker.res
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
// Minimal `node:worker_threads` binding for gwt watch re-runs.
|
|
2
|
-
//
|
|
3
|
-
// Node's ESM module registry is keyed by URL and never evicts, so re-importing a
|
|
4
|
-
// recompiled test file in the same process replays tests that close over the
|
|
5
|
-
// *old* instances of their statically-imported implementation modules (the
|
|
6
|
-
// Loader can only cache-bust the test file's own URL, not its transitive
|
|
7
|
-
// imports). Running each watch re-run in a short-lived Worker gives it a fresh
|
|
8
|
-
// registry — the recompiled modules are re-imported — and reclaims the whole
|
|
9
|
-
// imported graph on worker exit (fixing the monotonic-buster memory leak).
|
|
10
|
-
|
|
11
|
-
type t
|
|
12
|
-
type workerOptions<'a> = {workerData: 'a}
|
|
13
|
-
type url
|
|
14
|
-
|
|
15
|
-
@new @module("node:worker_threads")
|
|
16
|
-
external make: (url, workerOptions<'a>) => t = "Worker"
|
|
17
|
-
|
|
18
|
-
// A worker's stdout is piped to the parent's process.stdout by default, so the
|
|
19
|
-
// NDJSON the formatters write inside the worker reaches the client unchanged.
|
|
20
|
-
@send external on: (t, string, 'cb) => unit = "on"
|
|
21
|
-
@send external terminate: t => promise<int> = "terminate"
|
|
22
|
-
|
|
23
|
-
// The compiled worker entry lives next to this module in the in-source build.
|
|
24
|
-
let runWorkerUrl: url = %raw(`new URL("./RunWorker.res.mjs", import.meta.url)`)
|
package/src/Worker.res.mjs
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
// Unit tests for BuildClassifier — the state machine that turns a
|
|
2
|
-
// `rescript build -w` line-stream into buildStart / buildOk / buildFail. The
|
|
3
|
-
// success path is synchronous (the "Finished … compilation" line); the failure
|
|
4
|
-
// path is debounced (no terminator in rescript's error output), so those tests
|
|
5
|
-
// await past the 400ms settle window.
|
|
6
|
-
|
|
7
|
-
open JestGlobals
|
|
8
|
-
|
|
9
|
-
@val external setTimeout: (unit => unit, int) => unit = "setTimeout"
|
|
10
|
-
let delay = (ms: int): promise<unit> =>
|
|
11
|
-
Promise.make((resolve, _reject) => setTimeout(() => resolve(), ms))
|
|
12
|
-
|
|
13
|
-
describe("BuildClassifier", () => {
|
|
14
|
-
testPromise("emits onStart then onOk for a clean incremental compile", async () => {
|
|
15
|
-
let starts = ref(0)
|
|
16
|
-
let oks = ref(0)
|
|
17
|
-
let fails = ref([])
|
|
18
|
-
let feed = BuildClassifier.make({
|
|
19
|
-
onStart: () => starts := starts.contents + 1,
|
|
20
|
-
onOk: _ms => oks := oks.contents + 1,
|
|
21
|
-
onFail: msg => fails := Array.concat(fails.contents, [msg]),
|
|
22
|
-
})
|
|
23
|
-
feed("Parsed 1 source files")
|
|
24
|
-
feed("Compiled 1 modules")
|
|
25
|
-
feed("Finished incremental compilation")
|
|
26
|
-
expect(starts.contents)->toEqual(1)
|
|
27
|
-
expect(oks.contents)->toEqual(1)
|
|
28
|
-
expect(fails.contents->Array.length)->toEqual(0)
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
testPromise("emits onFail with the error text after the output settles", async () => {
|
|
32
|
-
let oks = ref(0)
|
|
33
|
-
let fails = ref([])
|
|
34
|
-
let feed = BuildClassifier.make({
|
|
35
|
-
onStart: () => (),
|
|
36
|
-
onOk: _ms => oks := oks.contents + 1,
|
|
37
|
-
onFail: msg => fails := Array.concat(fails.contents, [msg]),
|
|
38
|
-
})
|
|
39
|
-
feed("Parsed 1 source files")
|
|
40
|
-
feed("Compiled 1 modules")
|
|
41
|
-
feed("We've found a bug for you!")
|
|
42
|
-
feed("This has type: string")
|
|
43
|
-
feed("But it's expected to have type: int")
|
|
44
|
-
await delay(550)
|
|
45
|
-
expect(oks.contents)->toEqual(0)
|
|
46
|
-
expect(fails.contents->Array.length)->toEqual(1)
|
|
47
|
-
let msg = fails.contents->Array.getUnsafe(0)
|
|
48
|
-
expect(msg->String.includes("This has type: string"))->toEqual(true)
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
testPromise("watchdog fails a build that starts but never terminates", async () => {
|
|
52
|
-
let oks = ref(0)
|
|
53
|
-
let fails = ref([])
|
|
54
|
-
let feed = BuildClassifier.make(~watchdogMs=50, {
|
|
55
|
-
onStart: () => (),
|
|
56
|
-
onOk: _ms => oks := oks.contents + 1,
|
|
57
|
-
onFail: msg => fails := Array.concat(fails.contents, [msg]),
|
|
58
|
-
})
|
|
59
|
-
feed("Parsed 1 source files")
|
|
60
|
-
// No "Finished …" and no error marker — a crashed/hung watcher.
|
|
61
|
-
await delay(120)
|
|
62
|
-
expect(oks.contents)->toEqual(0)
|
|
63
|
-
expect(fails.contents->Array.length)->toEqual(1)
|
|
64
|
-
let msg = fails.contents->Array.getUnsafe(0)
|
|
65
|
-
expect(msg->String.includes("watchdog"))->toEqual(true)
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
testPromise("watchdog does not fire once the build finishes", async () => {
|
|
69
|
-
let oks = ref(0)
|
|
70
|
-
let fails = ref([])
|
|
71
|
-
let feed = BuildClassifier.make(~watchdogMs=50, {
|
|
72
|
-
onStart: () => (),
|
|
73
|
-
onOk: _ms => oks := oks.contents + 1,
|
|
74
|
-
onFail: msg => fails := Array.concat(fails.contents, [msg]),
|
|
75
|
-
})
|
|
76
|
-
feed("Parsed 1 source files")
|
|
77
|
-
feed("Finished incremental compilation")
|
|
78
|
-
await delay(120)
|
|
79
|
-
expect(oks.contents)->toEqual(1)
|
|
80
|
-
expect(fails.contents->Array.length)->toEqual(0)
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
testPromise("errors emitted before the first Parsed line still settle to onFail (B3)", async () => {
|
|
84
|
-
// A crashed toolchain / pnpm resolution failure prints an error block with no
|
|
85
|
-
// preceding "Parsed …" line. The old `compiling && hasError` guard left this
|
|
86
|
-
// pinned forever; now `hasError` alone settles it.
|
|
87
|
-
let oks = ref(0)
|
|
88
|
-
let fails = ref([])
|
|
89
|
-
let feed = BuildClassifier.make({
|
|
90
|
-
onStart: () => (),
|
|
91
|
-
onOk: _ms => oks := oks.contents + 1,
|
|
92
|
-
onFail: msg => fails := Array.concat(fails.contents, [msg]),
|
|
93
|
-
})
|
|
94
|
-
feed("Syntax error: unexpected token")
|
|
95
|
-
feed(" at Foo.res:3:1")
|
|
96
|
-
await delay(550)
|
|
97
|
-
expect(oks.contents)->toEqual(0)
|
|
98
|
-
expect(fails.contents->Array.length)->toEqual(1)
|
|
99
|
-
expect((fails.contents->Array.getUnsafe(0))->String.includes("Syntax error"))->toEqual(true)
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
testPromise("a multi-error build keeps error blocks past the old 20-line cap (B3)", async () => {
|
|
103
|
-
let fails = ref([])
|
|
104
|
-
let feed = BuildClassifier.make({
|
|
105
|
-
onStart: () => (),
|
|
106
|
-
onOk: _ms => (),
|
|
107
|
-
onFail: msg => fails := Array.concat(fails.contents, [msg]),
|
|
108
|
-
})
|
|
109
|
-
feed("Parsed 1 source files")
|
|
110
|
-
feed("We've found a bug for you!")
|
|
111
|
-
// 30 filler lines push a distinctive later error past the old 20-line window.
|
|
112
|
-
for i in 1 to 30 {
|
|
113
|
-
feed("noise line " ++ Int.toString(i))
|
|
114
|
-
}
|
|
115
|
-
feed("Found 2 errors")
|
|
116
|
-
await delay(550)
|
|
117
|
-
expect(fails.contents->Array.length)->toEqual(1)
|
|
118
|
-
// The late "Found 2 errors" line survives (dropped under the old cap of 20).
|
|
119
|
-
expect((fails.contents->Array.getUnsafe(0))->String.includes("Found 2 errors"))->toEqual(true)
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
testPromise("strips ANSI colour codes before matching the finish line", async () => {
|
|
123
|
-
let oks = ref(0)
|
|
124
|
-
let feed = BuildClassifier.make({
|
|
125
|
-
onStart: () => (),
|
|
126
|
-
onOk: _ms => oks := oks.contents + 1,
|
|
127
|
-
onFail: _msg => (),
|
|
128
|
-
})
|
|
129
|
-
feed("Parsed 2 source files")
|
|
130
|
-
feed("\x1b[1;32mFinished incremental compilation\x1b[0m")
|
|
131
|
-
expect(oks.contents)->toEqual(1)
|
|
132
|
-
})
|
|
133
|
-
})
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
-
|
|
3
|
-
import * as BuildClassifier$ReventlessGwt from "../src/BuildClassifier.res.mjs";
|
|
4
|
-
|
|
5
|
-
function delay(ms) {
|
|
6
|
-
return new Promise((resolve, _reject) => {
|
|
7
|
-
setTimeout(() => resolve(), ms);
|
|
8
|
-
});
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
globalThis.describe("BuildClassifier", () => {
|
|
12
|
-
globalThis.test("emits onStart then onOk for a clean incremental compile", async () => {
|
|
13
|
-
let starts = {
|
|
14
|
-
contents: 0
|
|
15
|
-
};
|
|
16
|
-
let oks = {
|
|
17
|
-
contents: 0
|
|
18
|
-
};
|
|
19
|
-
let fails = {
|
|
20
|
-
contents: []
|
|
21
|
-
};
|
|
22
|
-
let feed = BuildClassifier$ReventlessGwt.make(undefined, {
|
|
23
|
-
onStart: () => {
|
|
24
|
-
starts.contents = starts.contents + 1 | 0;
|
|
25
|
-
},
|
|
26
|
-
onOk: _ms => {
|
|
27
|
-
oks.contents = oks.contents + 1 | 0;
|
|
28
|
-
},
|
|
29
|
-
onFail: msg => {
|
|
30
|
-
fails.contents = fails.contents.concat([msg]);
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
feed("Parsed 1 source files");
|
|
34
|
-
feed("Compiled 1 modules");
|
|
35
|
-
feed("Finished incremental compilation");
|
|
36
|
-
globalThis.expect(starts.contents).toEqual(1);
|
|
37
|
-
globalThis.expect(oks.contents).toEqual(1);
|
|
38
|
-
globalThis.expect(fails.contents.length).toEqual(0);
|
|
39
|
-
});
|
|
40
|
-
globalThis.test("emits onFail with the error text after the output settles", async () => {
|
|
41
|
-
let oks = {
|
|
42
|
-
contents: 0
|
|
43
|
-
};
|
|
44
|
-
let fails = {
|
|
45
|
-
contents: []
|
|
46
|
-
};
|
|
47
|
-
let feed = BuildClassifier$ReventlessGwt.make(undefined, {
|
|
48
|
-
onStart: () => {},
|
|
49
|
-
onOk: _ms => {
|
|
50
|
-
oks.contents = oks.contents + 1 | 0;
|
|
51
|
-
},
|
|
52
|
-
onFail: msg => {
|
|
53
|
-
fails.contents = fails.contents.concat([msg]);
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
feed("Parsed 1 source files");
|
|
57
|
-
feed("Compiled 1 modules");
|
|
58
|
-
feed("We've found a bug for you!");
|
|
59
|
-
feed("This has type: string");
|
|
60
|
-
feed("But it's expected to have type: int");
|
|
61
|
-
await delay(550);
|
|
62
|
-
globalThis.expect(oks.contents).toEqual(0);
|
|
63
|
-
globalThis.expect(fails.contents.length).toEqual(1);
|
|
64
|
-
let msg = fails.contents[0];
|
|
65
|
-
globalThis.expect(msg.includes("This has type: string")).toEqual(true);
|
|
66
|
-
});
|
|
67
|
-
globalThis.test("watchdog fails a build that starts but never terminates", async () => {
|
|
68
|
-
let oks = {
|
|
69
|
-
contents: 0
|
|
70
|
-
};
|
|
71
|
-
let fails = {
|
|
72
|
-
contents: []
|
|
73
|
-
};
|
|
74
|
-
let feed = BuildClassifier$ReventlessGwt.make(50, {
|
|
75
|
-
onStart: () => {},
|
|
76
|
-
onOk: _ms => {
|
|
77
|
-
oks.contents = oks.contents + 1 | 0;
|
|
78
|
-
},
|
|
79
|
-
onFail: msg => {
|
|
80
|
-
fails.contents = fails.contents.concat([msg]);
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
feed("Parsed 1 source files");
|
|
84
|
-
await delay(120);
|
|
85
|
-
globalThis.expect(oks.contents).toEqual(0);
|
|
86
|
-
globalThis.expect(fails.contents.length).toEqual(1);
|
|
87
|
-
let msg = fails.contents[0];
|
|
88
|
-
globalThis.expect(msg.includes("watchdog")).toEqual(true);
|
|
89
|
-
});
|
|
90
|
-
globalThis.test("watchdog does not fire once the build finishes", async () => {
|
|
91
|
-
let oks = {
|
|
92
|
-
contents: 0
|
|
93
|
-
};
|
|
94
|
-
let fails = {
|
|
95
|
-
contents: []
|
|
96
|
-
};
|
|
97
|
-
let feed = BuildClassifier$ReventlessGwt.make(50, {
|
|
98
|
-
onStart: () => {},
|
|
99
|
-
onOk: _ms => {
|
|
100
|
-
oks.contents = oks.contents + 1 | 0;
|
|
101
|
-
},
|
|
102
|
-
onFail: msg => {
|
|
103
|
-
fails.contents = fails.contents.concat([msg]);
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
feed("Parsed 1 source files");
|
|
107
|
-
feed("Finished incremental compilation");
|
|
108
|
-
await delay(120);
|
|
109
|
-
globalThis.expect(oks.contents).toEqual(1);
|
|
110
|
-
globalThis.expect(fails.contents.length).toEqual(0);
|
|
111
|
-
});
|
|
112
|
-
globalThis.test("errors emitted before the first Parsed line still settle to onFail (B3)", async () => {
|
|
113
|
-
let oks = {
|
|
114
|
-
contents: 0
|
|
115
|
-
};
|
|
116
|
-
let fails = {
|
|
117
|
-
contents: []
|
|
118
|
-
};
|
|
119
|
-
let feed = BuildClassifier$ReventlessGwt.make(undefined, {
|
|
120
|
-
onStart: () => {},
|
|
121
|
-
onOk: _ms => {
|
|
122
|
-
oks.contents = oks.contents + 1 | 0;
|
|
123
|
-
},
|
|
124
|
-
onFail: msg => {
|
|
125
|
-
fails.contents = fails.contents.concat([msg]);
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
feed("Syntax error: unexpected token");
|
|
129
|
-
feed(" at Foo.res:3:1");
|
|
130
|
-
await delay(550);
|
|
131
|
-
globalThis.expect(oks.contents).toEqual(0);
|
|
132
|
-
globalThis.expect(fails.contents.length).toEqual(1);
|
|
133
|
-
globalThis.expect(fails.contents[0].includes("Syntax error")).toEqual(true);
|
|
134
|
-
});
|
|
135
|
-
globalThis.test("a multi-error build keeps error blocks past the old 20-line cap (B3)", async () => {
|
|
136
|
-
let fails = {
|
|
137
|
-
contents: []
|
|
138
|
-
};
|
|
139
|
-
let feed = BuildClassifier$ReventlessGwt.make(undefined, {
|
|
140
|
-
onStart: () => {},
|
|
141
|
-
onOk: _ms => {},
|
|
142
|
-
onFail: msg => {
|
|
143
|
-
fails.contents = fails.contents.concat([msg]);
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
feed("Parsed 1 source files");
|
|
147
|
-
feed("We've found a bug for you!");
|
|
148
|
-
for (let i = 1; i <= 30; ++i) {
|
|
149
|
-
feed("noise line " + i.toString());
|
|
150
|
-
}
|
|
151
|
-
feed("Found 2 errors");
|
|
152
|
-
await delay(550);
|
|
153
|
-
globalThis.expect(fails.contents.length).toEqual(1);
|
|
154
|
-
globalThis.expect(fails.contents[0].includes("Found 2 errors")).toEqual(true);
|
|
155
|
-
});
|
|
156
|
-
globalThis.test("strips ANSI colour codes before matching the finish line", async () => {
|
|
157
|
-
let oks = {
|
|
158
|
-
contents: 0
|
|
159
|
-
};
|
|
160
|
-
let feed = BuildClassifier$ReventlessGwt.make(undefined, {
|
|
161
|
-
onStart: () => {},
|
|
162
|
-
onOk: _ms => {
|
|
163
|
-
oks.contents = oks.contents + 1 | 0;
|
|
164
|
-
},
|
|
165
|
-
onFail: _msg => {}
|
|
166
|
-
});
|
|
167
|
-
feed("Parsed 2 source files");
|
|
168
|
-
feed("\x1b[1;32mFinished incremental compilation\x1b[0m");
|
|
169
|
-
globalThis.expect(oks.contents).toEqual(1);
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
export {
|
|
174
|
-
delay,
|
|
175
|
-
}
|
|
176
|
-
/* Not a pure module */
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
// Regression tests for the CLI runner's per-test deadline (A2) and the
|
|
2
|
-
// Collector's skip-depth reset (A4). Before A2 a hung test body wedged the whole
|
|
3
|
-
// run — `runEntry` awaited it with no deadline. Before A4 a throwing `xdescribe`
|
|
4
|
-
// left `skipDepth` > 0, silently skipping every subsequently loaded file; the
|
|
5
|
-
// reset lives in `Collector.activate`.
|
|
6
|
-
|
|
7
|
-
open JestGlobals
|
|
8
|
-
|
|
9
|
-
@val external setTimeout: (unit => unit, int) => unit = "setTimeout"
|
|
10
|
-
|
|
11
|
-
let hangingEntry = (~timeout): Collector.entry => {
|
|
12
|
-
id: "hangs",
|
|
13
|
-
name: "hangs forever",
|
|
14
|
-
describePath: [],
|
|
15
|
-
slice: None,
|
|
16
|
-
// A body that never resolves — the pathological hung test.
|
|
17
|
-
body: () => Promise.make((_resolve, _reject) => ()),
|
|
18
|
-
status: Collector.Runnable,
|
|
19
|
-
location: None,
|
|
20
|
-
timeout,
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
describe("Cli.runEntry deadline", () => {
|
|
24
|
-
testPromise("a hung body is reported as a timeout, not a wedge", async () => {
|
|
25
|
-
let r = await Cli.runEntry(hangingEntry(~timeout=Some(50)))
|
|
26
|
-
expect(r.status)->toEqual(RunnerTypes.Fail)
|
|
27
|
-
switch r.mismatch {
|
|
28
|
-
| Some(Outcome.Throw({error})) => expect(error->String.includes("timed out"))->toEqual(true)
|
|
29
|
-
| _ => JsError.throwWithMessage("expected a timeout Throw mismatch")
|
|
30
|
-
}
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
testPromise("a body that resolves within the deadline passes", async () => {
|
|
34
|
-
let entry: Collector.entry = {
|
|
35
|
-
id: "ok",
|
|
36
|
-
name: "resolves fast",
|
|
37
|
-
describePath: [],
|
|
38
|
-
slice: None,
|
|
39
|
-
body: () => Promise.resolve(Outcome.pass),
|
|
40
|
-
status: Collector.Runnable,
|
|
41
|
-
location: None,
|
|
42
|
-
timeout: Some(1000),
|
|
43
|
-
}
|
|
44
|
-
let r = await Cli.runEntry(entry)
|
|
45
|
-
expect(r.status)->toEqual(RunnerTypes.Pass)
|
|
46
|
-
})
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
describe("Collector.activate", () => {
|
|
50
|
-
testSync("resets a leaked skipDepth to zero", () => {
|
|
51
|
-
// Simulate the leak a throwing xdescribe body would leave behind.
|
|
52
|
-
Collector.skipDepth := 3
|
|
53
|
-
Collector.activate()
|
|
54
|
-
expect(Collector.skipDepth.contents)->toEqual(0)
|
|
55
|
-
Collector.deactivate()
|
|
56
|
-
})
|
|
57
|
-
})
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
-
|
|
3
|
-
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
4
|
-
import * as Cli$ReventlessGwt from "../src/Cli.res.mjs";
|
|
5
|
-
import * as Outcome$ReventlessGwt from "../src/Outcome.res.mjs";
|
|
6
|
-
import * as Collector$ReventlessGwt from "../src/Collector.res.mjs";
|
|
7
|
-
|
|
8
|
-
function hangingEntry(timeout) {
|
|
9
|
-
return {
|
|
10
|
-
id: "hangs",
|
|
11
|
-
name: "hangs forever",
|
|
12
|
-
describePath: [],
|
|
13
|
-
slice: undefined,
|
|
14
|
-
body: () => new Promise((_resolve, _reject) => {}),
|
|
15
|
-
status: "Runnable",
|
|
16
|
-
location: undefined,
|
|
17
|
-
timeout: timeout
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
globalThis.describe("Cli.runEntry deadline", () => {
|
|
22
|
-
globalThis.test("a hung body is reported as a timeout, not a wedge", async () => {
|
|
23
|
-
let r = await Cli$ReventlessGwt.runEntry(hangingEntry(50));
|
|
24
|
-
globalThis.expect(r.status).toEqual("Fail");
|
|
25
|
-
let match = r.mismatch;
|
|
26
|
-
if (match === undefined) {
|
|
27
|
-
return Stdlib_JsError.throwWithMessage("expected a timeout Throw mismatch");
|
|
28
|
-
}
|
|
29
|
-
if (match.TAG !== "Throw") {
|
|
30
|
-
return Stdlib_JsError.throwWithMessage("expected a timeout Throw mismatch");
|
|
31
|
-
}
|
|
32
|
-
globalThis.expect(match.error.includes("timed out")).toEqual(true);
|
|
33
|
-
});
|
|
34
|
-
globalThis.test("a body that resolves within the deadline passes", async () => {
|
|
35
|
-
let entry_describePath = [];
|
|
36
|
-
let entry_body = () => Promise.resolve(Outcome$ReventlessGwt.pass);
|
|
37
|
-
let entry_timeout = 1000;
|
|
38
|
-
let entry = {
|
|
39
|
-
id: "ok",
|
|
40
|
-
name: "resolves fast",
|
|
41
|
-
describePath: entry_describePath,
|
|
42
|
-
slice: undefined,
|
|
43
|
-
body: entry_body,
|
|
44
|
-
status: "Runnable",
|
|
45
|
-
location: undefined,
|
|
46
|
-
timeout: entry_timeout
|
|
47
|
-
};
|
|
48
|
-
let r = await Cli$ReventlessGwt.runEntry(entry);
|
|
49
|
-
globalThis.expect(r.status).toEqual("Pass");
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
globalThis.describe("Collector.activate", () => {
|
|
54
|
-
globalThis.test("resets a leaked skipDepth to zero", () => {
|
|
55
|
-
Collector$ReventlessGwt.skipDepth.contents = 3;
|
|
56
|
-
Collector$ReventlessGwt.activate();
|
|
57
|
-
globalThis.expect(Collector$ReventlessGwt.skipDepth.contents).toEqual(0);
|
|
58
|
-
Collector$ReventlessGwt.deactivate();
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
export {
|
|
63
|
-
hangingEntry,
|
|
64
|
-
}
|
|
65
|
-
/* Not a pure module */
|