qunitx-cli 0.23.0 → 0.23.2
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/README.md +1 -1
- package/dist/cli.js +63 -23
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -115,7 +115,7 @@ What it does: cold-start cost dominates a single `qunitx` run — launching Chro
|
|
|
115
115
|
|
|
116
116
|
A single one-off run won't get faster from spinning the daemon up; it's an opt-in optimization aimed at two situations:
|
|
117
117
|
|
|
118
|
-
- **Local TDD loops.** Export `QUNITX_DAEMON=1` in your shell profile (or run `qunitx daemon start` once at the top of your session) and forget about it — subsequent runs reuse the daemon automatically until you `daemon stop` or 30 idle minutes pass.
|
|
118
|
+
- **Local TDD loops.** Export `QUNITX_DAEMON=1` in your shell profile (or run `qunitx daemon start` once at the top of your session) and forget about it — subsequent runs reuse the daemon automatically until you `daemon stop` or 30 idle minutes pass. Override the idle window with `QUNITX_DAEMON_IDLE_TIMEOUT` (`1h`, `45s`, `500ms`; bare numbers are minutes), or set it to `false` to disable auto-shutdown entirely.
|
|
119
119
|
- **Monorepo CI** where each package shells out to `qunitx` separately. Set `QUNITX_DAEMON=1` for the job and a single daemon is auto-spawned and reused across every package's invocation.
|
|
120
120
|
|
|
121
121
|
```sh
|
package/dist/cli.js
CHANGED
|
@@ -374,7 +374,7 @@ var init_package = __esm({
|
|
|
374
374
|
package_default = {
|
|
375
375
|
name: "qunitx-cli",
|
|
376
376
|
type: "module",
|
|
377
|
-
version: "0.23.
|
|
377
|
+
version: "0.23.2",
|
|
378
378
|
description: "Browser runner for QUnitx: run your qunitx tests in google-chrome",
|
|
379
379
|
author: "Izel Nakri",
|
|
380
380
|
license: "MIT",
|
|
@@ -748,6 +748,31 @@ var init_init = __esm({
|
|
|
748
748
|
}
|
|
749
749
|
});
|
|
750
750
|
|
|
751
|
+
// lib/utils/parse-daemon-idle-timeout.ts
|
|
752
|
+
function parseDaemonIdleTimeout(value) {
|
|
753
|
+
if (!value) return { ms: DEFAULT_DAEMON_IDLE_TIMEOUT_MS, warning: null };
|
|
754
|
+
if (/^\s*false\s*$/i.test(value)) return { ms: Infinity, warning: null };
|
|
755
|
+
const match = /^\s*(\d+(?:\.\d+)?)\s*(ms|s|m|h)?\s*$/i.exec(value);
|
|
756
|
+
if (!match) return invalid(value);
|
|
757
|
+
const n = Number(match[1]);
|
|
758
|
+
if (!(n > 0)) return invalid(value);
|
|
759
|
+
const unit = (match[2] ?? "m").toLowerCase();
|
|
760
|
+
return { ms: Math.round(n * UNIT_TO_MS[unit]), warning: null };
|
|
761
|
+
}
|
|
762
|
+
function invalid(value) {
|
|
763
|
+
return {
|
|
764
|
+
ms: DEFAULT_DAEMON_IDLE_TIMEOUT_MS,
|
|
765
|
+
warning: `\u26A0 qunitx: QUNITX_DAEMON_IDLE_TIMEOUT=${JSON.stringify(value)} is not a valid duration; using 30-minute default. Accepted: bare number = minutes, "30m", "1h", "45s", "500ms", "false" (no auto-shutdown).`
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
var UNIT_TO_MS, DEFAULT_DAEMON_IDLE_TIMEOUT_MS;
|
|
769
|
+
var init_parse_daemon_idle_timeout = __esm({
|
|
770
|
+
"lib/utils/parse-daemon-idle-timeout.ts"() {
|
|
771
|
+
UNIT_TO_MS = { ms: 1, s: 1e3, m: 6e4, h: 36e5 };
|
|
772
|
+
DEFAULT_DAEMON_IDLE_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
773
|
+
}
|
|
774
|
+
});
|
|
775
|
+
|
|
751
776
|
// lib/utils/close-with-grace.ts
|
|
752
777
|
function closeWithGrace(closes, graceMs = CLEANUP_GRACE_MS) {
|
|
753
778
|
return new Promise((resolve) => {
|
|
@@ -3573,19 +3598,26 @@ function setupFileWatchers(testFileLookupPaths, config, onEventFunc, onFinishFun
|
|
|
3573
3598
|
const parentDir = path9.dirname(watchPath);
|
|
3574
3599
|
const watchedBasename = path9.basename(watchPath);
|
|
3575
3600
|
let parentUnlinkFired = false;
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
if (parentUnlinkFired) return;
|
|
3601
|
+
let rescanTimer = null;
|
|
3602
|
+
const tryFireParentUnlink = async () => {
|
|
3603
|
+
if (parentUnlinkFired) return false;
|
|
3579
3604
|
parentUnlinkFired = true;
|
|
3580
3605
|
try {
|
|
3581
3606
|
await stat(watchPath);
|
|
3582
3607
|
parentUnlinkFired = false;
|
|
3608
|
+
return false;
|
|
3583
3609
|
} catch {
|
|
3584
3610
|
handleWatchEvent(config, extensions, "unlinkDir", watchPath, onEventFunc, onFinishFunc);
|
|
3585
3611
|
childWatcher.close();
|
|
3586
3612
|
parentWatcher.close();
|
|
3613
|
+
if (rescanTimer) clearInterval(rescanTimer);
|
|
3587
3614
|
delete fileWatchers[watchPath];
|
|
3615
|
+
return true;
|
|
3588
3616
|
}
|
|
3617
|
+
};
|
|
3618
|
+
const parentWatcher = fs11.watch(parentDir, async (eventType, filename) => {
|
|
3619
|
+
if (!ready || filename !== watchedBasename || eventType !== "rename") return;
|
|
3620
|
+
await tryFireParentUnlink();
|
|
3589
3621
|
});
|
|
3590
3622
|
parentWatchers.push(parentWatcher);
|
|
3591
3623
|
fileWatchers[watchPath] = childWatcher;
|
|
@@ -3598,22 +3630,23 @@ function setupFileWatchers(testFileLookupPaths, config, onEventFunc, onFinishFun
|
|
|
3598
3630
|
)
|
|
3599
3631
|
);
|
|
3600
3632
|
if (process.platform === "darwin") {
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
);
|
|
3633
|
+
rescanTimer = setInterval(async () => {
|
|
3634
|
+
if (!ready || rescanInProgress || parentUnlinkFired) return;
|
|
3635
|
+
if (await tryFireParentUnlink()) return;
|
|
3636
|
+
rescanInProgress = true;
|
|
3637
|
+
rescanDirectoryForDelta(
|
|
3638
|
+
watchPath,
|
|
3639
|
+
config,
|
|
3640
|
+
extensions,
|
|
3641
|
+
onEventFunc,
|
|
3642
|
+
onFinishFunc,
|
|
3643
|
+
trackSymlink
|
|
3644
|
+
).finally(() => {
|
|
3645
|
+
rescanInProgress = false;
|
|
3646
|
+
});
|
|
3647
|
+
}, RESCAN_INTERVAL_MS);
|
|
3648
|
+
rescanTimer.unref();
|
|
3649
|
+
rescanTimers.push(rescanTimer);
|
|
3617
3650
|
}
|
|
3618
3651
|
}
|
|
3619
3652
|
readyPromises.push(
|
|
@@ -4453,6 +4486,7 @@ async function shutdownDaemon2(state, reason) {
|
|
|
4453
4486
|
}
|
|
4454
4487
|
function resetIdleTimer(state) {
|
|
4455
4488
|
if (state.idleTimer) clearTimeout(state.idleTimer);
|
|
4489
|
+
if (!Number.isFinite(IDLE_TIMEOUT_MS)) return;
|
|
4456
4490
|
state.idleTimer = setTimeout(() => void shutdownDaemon2(state, "idle timeout"), IDLE_TIMEOUT_MS);
|
|
4457
4491
|
state.idleTimer.unref();
|
|
4458
4492
|
}
|
|
@@ -4626,12 +4660,13 @@ var IDLE_TIMEOUT_MS, LIVENESS_PROBE_TIMEOUT_MS, MAX_CONSECUTIVE_CRASHES;
|
|
|
4626
4660
|
var init_server = __esm({
|
|
4627
4661
|
"lib/commands/daemon/server.ts"() {
|
|
4628
4662
|
init_daemon_socket_path();
|
|
4663
|
+
init_parse_daemon_idle_timeout();
|
|
4629
4664
|
init_socket_utils();
|
|
4630
4665
|
init_config();
|
|
4631
4666
|
init_browser();
|
|
4632
4667
|
init_tests_in_browser();
|
|
4633
4668
|
init_run();
|
|
4634
|
-
IDLE_TIMEOUT_MS =
|
|
4669
|
+
IDLE_TIMEOUT_MS = parseDaemonIdleTimeout(process.env.QUNITX_DAEMON_IDLE_TIMEOUT).ms;
|
|
4635
4670
|
LIVENESS_PROBE_TIMEOUT_MS = 500;
|
|
4636
4671
|
MAX_CONSECUTIVE_CRASHES = 2;
|
|
4637
4672
|
}
|
|
@@ -4682,6 +4717,8 @@ function waitForFile(filePath, timeoutMs) {
|
|
|
4682
4717
|
});
|
|
4683
4718
|
}
|
|
4684
4719
|
async function spawnAndWaitForDaemon() {
|
|
4720
|
+
const parsed = parseDaemonIdleTimeout(process.env.QUNITX_DAEMON_IDLE_TIMEOUT);
|
|
4721
|
+
if (parsed.warning) process.stderr.write(parsed.warning + "\n");
|
|
4685
4722
|
spawn3(process.execPath, [CLI_ENTRY, "daemon", "_serve"], {
|
|
4686
4723
|
detached: true,
|
|
4687
4724
|
stdio: "ignore",
|
|
@@ -4740,6 +4777,7 @@ var init_daemon = __esm({
|
|
|
4740
4777
|
"lib/commands/daemon/index.ts"() {
|
|
4741
4778
|
init_color();
|
|
4742
4779
|
init_daemon_socket_path();
|
|
4780
|
+
init_parse_daemon_idle_timeout();
|
|
4743
4781
|
init_client();
|
|
4744
4782
|
init_package();
|
|
4745
4783
|
SPAWN_TIMEOUT_MS = 3e4;
|
|
@@ -4753,8 +4791,10 @@ ${color2("$ qunitx daemon stop")} # Stop the running daemon
|
|
|
4753
4791
|
${color2("$ qunitx daemon status")} # Print pid, socket, and uptime
|
|
4754
4792
|
|
|
4755
4793
|
${highlight2("Environment:")}
|
|
4756
|
-
${color2("QUNITX_DAEMON=1")}
|
|
4757
|
-
${color2("QUNITX_NO_DAEMON=1")}
|
|
4794
|
+
${color2("QUNITX_DAEMON=1")} : auto-spawn the daemon on the first qunitx run; reuse it on every run after (overrides the CI=1 bypass)
|
|
4795
|
+
${color2("QUNITX_NO_DAEMON=1")} : never use the daemon for this run
|
|
4796
|
+
${color2("QUNITX_DAEMON_IDLE_TIMEOUT")} : idle window before self-shutdown (default 30m). Bare number = minutes; ${color2("ms")} / ${color2("s")} / ${color2("m")} / ${color2("h")} suffixes accepted (${color2("1h")}, ${color2("45s")}, ${color2("500ms")}). Set to ${color2("false")} to disable auto-shutdown. Read at daemon spawn; invalid values warn and fall back to the default.
|
|
4797
|
+
${color2("QUNITX_DAEMON_LOG=<path>")} : redirect the daemon's stdout + stderr to a file (otherwise lost to the detached spawn)
|
|
4758
4798
|
|
|
4759
4799
|
${highlight2("Tip:")} set ${color2("QUNITX_DAEMON=1")} to auto-spawn the daemon on the first qunitx run; ${color2("$ qunitx --help")} for top-level options.
|
|
4760
4800
|
`;
|