@rstest/browser 0.11.2 → 0.11.4
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/dist/browser-container/container-static/js/{117.3da95caa39.js → 243.a8eed2b9e7.js} +936 -910
- package/dist/browser-container/container-static/js/243.a8eed2b9e7.js.LICENSE.txt +1 -0
- package/dist/browser-container/container-static/js/{index.6e4da8c9ab.js → index.84aaafaf21.js} +80 -75
- package/dist/browser-container/index.html +1 -1
- package/dist/index.js +263 -148
- package/package.json +10 -9
- package/src/browserExecutor.ts +5 -3
- package/src/client/entry.ts +21 -13
- package/src/configValidation.ts +6 -0
- package/src/hostController.ts +496 -129
- package/dist/browser-container/container-static/js/117.3da95caa39.js.LICENSE.txt +0 -1
- package/dist/watchCliShortcuts.d.ts +0 -6
- package/src/AGENTS.md +0 -150
- package/src/client/AGENTS.md +0 -89
- package/src/client/formatConsole.ts +0 -77
- package/src/client/public.ts +0 -12
- package/src/watchCliShortcuts.ts +0 -77
|
@@ -1 +0,0 @@
|
|
|
1
|
-
/*! LICENSE: 117.3da95caa39.js.LICENSE.txt */
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
export declare const isBrowserWatchCliShortcutsEnabled: () => boolean;
|
|
2
|
-
export declare const getBrowserWatchCliShortcutsHintMessage: () => string;
|
|
3
|
-
export declare const logBrowserWatchReadyMessage: (enableCliShortcuts: boolean) => void;
|
|
4
|
-
export declare function setupBrowserWatchCliShortcuts({ close }: {
|
|
5
|
-
close: () => Promise<void>;
|
|
6
|
-
}): Promise<() => void>;
|
package/src/AGENTS.md
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
# Browser mode host architecture
|
|
2
|
-
|
|
3
|
-
This document is architecture-only and focuses on browser mode scheduling in `@rstest/browser` host-side modules.
|
|
4
|
-
|
|
5
|
-
## Run lifecycle ownership (browser/node isomorphism)
|
|
6
|
-
|
|
7
|
-
`runBrowserController` splits finalize ownership on `context.command`:
|
|
8
|
-
|
|
9
|
-
- **Non-watch runs**: the host never self-finalizes. It returns a fully-populated `BrowserTestRunResult` with a deferred `close`, and `@rstest/core`'s `finalizeRunCycle` reduces the run's `ExecutorCycleOutcome`s into the verdict — reporter `onTestRunEnd`, coverage merge + report, exit code, and the bail message. `BrowserTestRunOptions.skipOnTestRunEnd` (deprecated and ignored since the finalize unification) has been removed.
|
|
10
|
-
- **Watch runs**: the host owns the per-rerun lifecycle (`onTestRunStart`/`onTestRunEnd` per rerun) and self-finalizes; core skips its finalize entirely for browser-only and zero-node mixed watch runs.
|
|
11
|
-
|
|
12
|
-
Runner lifecycle events flow through per-project `RunnerEventSink`s (`createRunnerEventSink` from core — the same event pump the node pool RPC uses). The host keeps a `Map<projectName, RunnerEventSink>` and resolves sinks via `sinkForProjectName`/`sinkForTestPath` (falling back to the first project for unknown names); it never fans out to reporters or `stateManager` directly.
|
|
13
|
-
|
|
14
|
-
Cross-file `bail` is enforced at file boundaries in the headless scheduler: before picking up the next file, each worker checks the cycle-wide `stateManager.getCountOfFailedTests()` and drains the remaining queue as skipped results once the budget is reached. The headed debugging UI does not apply bail; within a running file, the runner's per-test gate uses the client-local failed count (see `src/client/AGENTS.md`).
|
|
15
|
-
|
|
16
|
-
## Module topology
|
|
17
|
-
|
|
18
|
-
```mermaid
|
|
19
|
-
flowchart LR
|
|
20
|
-
subgraph Host["@rstest/browser host (Node.js)"]
|
|
21
|
-
IDX["index.ts\nrunBrowserTests()"]
|
|
22
|
-
CV["configValidation.ts\nvalidateBrowserConfig()"]
|
|
23
|
-
HC["hostController.ts\nrunBrowserController()"]
|
|
24
|
-
RT["createBrowserRuntime()"]
|
|
25
|
-
DR["dispatchCapabilities.ts + dispatchRouter.ts\nnamespace router"]
|
|
26
|
-
BRR["browserRpcRegistry.ts\nBrowser RPC allowlists"]
|
|
27
|
-
RL["runSession.ts\nRunSessionLifecycle"]
|
|
28
|
-
SR["sessionRegistry.ts\nRunnerSessionRegistry"]
|
|
29
|
-
WP["watchRerunPlanner.ts"]
|
|
30
|
-
LS["headlessLatestRerunScheduler.ts"]
|
|
31
|
-
HT["headlessTransport.ts\nattachHeadlessRunnerTransport()"]
|
|
32
|
-
CC["concurrency.ts\ngetHeadlessConcurrency()"]
|
|
33
|
-
HQ["headedSerialTaskQueue.ts\ncreateHeadedSerialTaskQueue()"]
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
subgraph UI["@rstest/browser-ui container (headed path)"]
|
|
37
|
-
UR["useRpc() / birpc"]
|
|
38
|
-
CH["core/channel.ts\nforwardDispatchRpcRequest()"]
|
|
39
|
-
MH["main.tsx message listener\nforward lifecycle callbacks"]
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
subgraph Runner["runner runtime (src/client/entry.ts)"]
|
|
43
|
-
MSG["runner lifecycle messages"]
|
|
44
|
-
RPC["dispatch-rpc-request"]
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
IDX -."re-export; invoked by @rstest/core before the run".-> CV
|
|
48
|
-
IDX --> HC
|
|
49
|
-
HC --> RT
|
|
50
|
-
HC --> DR
|
|
51
|
-
DR --> BRR
|
|
52
|
-
HC --> WP
|
|
53
|
-
WP --> LS
|
|
54
|
-
HC --> RL
|
|
55
|
-
RL --> SR
|
|
56
|
-
HC --> CC
|
|
57
|
-
HC --> HQ
|
|
58
|
-
HC --> HT
|
|
59
|
-
|
|
60
|
-
UR <--> HC
|
|
61
|
-
MSG --> MH
|
|
62
|
-
MH --> UR
|
|
63
|
-
RPC --> CH
|
|
64
|
-
CH --> UR
|
|
65
|
-
|
|
66
|
-
HT -."headless bridge:\nexposeFunction(__rstest_dispatch__, __rstest_dispatch_rpc__)".-> Runner
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
## Headed transport path
|
|
70
|
-
|
|
71
|
-
Primary dispatch request direction is `Runner -> Container -> Host -> Router -> Handler`.
|
|
72
|
-
`Host -> Container` in this path is bootstrap setup and callback delivery, not router request initiation.
|
|
73
|
-
`dispatchRouter` handles inbound request routing only; outbound response delivery is a transport reply.
|
|
74
|
-
|
|
75
|
-
### Bootstrap control plane
|
|
76
|
-
|
|
77
|
-
```mermaid
|
|
78
|
-
sequenceDiagram
|
|
79
|
-
participant Host as browser hostController
|
|
80
|
-
participant Container as browser-ui container
|
|
81
|
-
|
|
82
|
-
Host->>Container: open container and establish birpc
|
|
83
|
-
Host->>Container: provide BrowserHostConfig
|
|
84
|
-
Container-->>Host: getTestFiles and rerun requests
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
### Runtime dispatch RPC data plane
|
|
88
|
-
|
|
89
|
-
```mermaid
|
|
90
|
-
sequenceDiagram
|
|
91
|
-
participant Runner as client iframe runner
|
|
92
|
-
participant Container as browser-ui channel
|
|
93
|
-
participant Host as browser hostController
|
|
94
|
-
participant Router as browser dispatchRouter
|
|
95
|
-
participant Handler as namespace handler
|
|
96
|
-
|
|
97
|
-
Runner->>Container: postMessage runner lifecycle
|
|
98
|
-
Container->>Host: forward lifecycle callbacks (onTest*)
|
|
99
|
-
Host->>Host: feed per-project RunnerEventSink (stateManager + reporters)
|
|
100
|
-
|
|
101
|
-
Runner->>Container: postMessage dispatch rpc request
|
|
102
|
-
Container->>Host: rpc.dispatch(request)
|
|
103
|
-
Host->>Router: routing inbound request
|
|
104
|
-
Router->>Handler: resolve namespace handler
|
|
105
|
-
Handler->>Host: execute host capability work
|
|
106
|
-
Host-->>Handler: capability result or error
|
|
107
|
-
Handler-->>Router: return handler result
|
|
108
|
-
Router-->>Host: routing done, response payload
|
|
109
|
-
Host-->>Container: transport reply payload
|
|
110
|
-
Container-->>Runner: transport reply to runner
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
## Headless transport path
|
|
114
|
-
|
|
115
|
-
Primary dispatch request direction is `Runner -> Host -> Router -> Handler`.
|
|
116
|
-
`Host -> Runner` in this path is bridge registration, not router request initiation.
|
|
117
|
-
`dispatchRouter` handles inbound request routing only; outbound response delivery is a transport reply.
|
|
118
|
-
|
|
119
|
-
### Bootstrap control plane
|
|
120
|
-
|
|
121
|
-
```mermaid
|
|
122
|
-
sequenceDiagram
|
|
123
|
-
participant Host as browser hostController
|
|
124
|
-
participant Runner as client top level runner
|
|
125
|
-
|
|
126
|
-
Host->>Runner: exposeFunction __rstest_dispatch__
|
|
127
|
-
Host->>Runner: exposeFunction __rstest_dispatch_rpc__
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### Runtime dispatch RPC data plane
|
|
131
|
-
|
|
132
|
-
```mermaid
|
|
133
|
-
sequenceDiagram
|
|
134
|
-
participant Runner as client top level runner
|
|
135
|
-
participant Host as browser hostController
|
|
136
|
-
participant Router as browser dispatchRouter
|
|
137
|
-
participant Handler as namespace handler
|
|
138
|
-
|
|
139
|
-
Runner->>Host: __rstest_dispatch__ lifecycle
|
|
140
|
-
Host->>Host: feed per-project RunnerEventSink (stateManager + reporters)
|
|
141
|
-
|
|
142
|
-
Runner->>Host: __rstest_dispatch_rpc__ request
|
|
143
|
-
Host->>Router: routing inbound request
|
|
144
|
-
Router->>Handler: resolve namespace handler
|
|
145
|
-
Handler->>Host: execute host capability work
|
|
146
|
-
Host-->>Handler: capability result or error
|
|
147
|
-
Handler-->>Router: return handler result
|
|
148
|
-
Router-->>Host: routing done, response payload
|
|
149
|
-
Host-->>Runner: transport reply payload
|
|
150
|
-
```
|
package/src/client/AGENTS.md
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
# Browser mode runner architecture
|
|
2
|
-
|
|
3
|
-
This document is architecture-only and focuses on the browser runner runtime in `src/client`.
|
|
4
|
-
|
|
5
|
-
## Runner bootstrap pipeline
|
|
6
|
-
|
|
7
|
-
```mermaid
|
|
8
|
-
flowchart TD
|
|
9
|
-
A["waitForConfig()"] --> B["read __RSTEST_BROWSER_OPTIONS__ + URL overrides"]
|
|
10
|
-
B --> R["send ready"]
|
|
11
|
-
R --> C["setRealTimers()"]
|
|
12
|
-
C --> D["preloadRunnerSourceMap()"]
|
|
13
|
-
D --> E["resolve project + runtimeConfig"]
|
|
14
|
-
E --> F{"execution mode"}
|
|
15
|
-
|
|
16
|
-
F -->|collect| G["create runtime + load setup/test modules + runner.collectTests()"]
|
|
17
|
-
G --> H["send collect-result / collect-complete"]
|
|
18
|
-
|
|
19
|
-
F -->|run| I["interceptConsole() + createRstestRuntime()"]
|
|
20
|
-
I --> J["send file-start"]
|
|
21
|
-
J --> K["load setup files + load test module"]
|
|
22
|
-
K --> L["runner.runTests() + send case-result"]
|
|
23
|
-
L --> N["send file-complete per file"]
|
|
24
|
-
N --> O["send complete after all files"]
|
|
25
|
-
|
|
26
|
-
H --> M["window.__RSTEST_DONE__ = true"]
|
|
27
|
-
O --> M
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## Transport architecture
|
|
31
|
-
|
|
32
|
-
```mermaid
|
|
33
|
-
flowchart LR
|
|
34
|
-
subgraph IframePath["Iframe path (headed)"]
|
|
35
|
-
S1["send()"] --> P1["parent.postMessage(__rstest_dispatch__)"]
|
|
36
|
-
R1["dispatchRunnerLifecycle()"] --> P2["postMessage dispatch-rpc-request"]
|
|
37
|
-
SN1["snapshot.ts / browserRpc.ts via dispatchTransport.dispatchRpc()"] --> P3["postMessage dispatch-rpc-request + wait __rstest_dispatch_response__"]
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
subgraph TopLevelRunPath["Top-level page path (headless run)"]
|
|
41
|
-
S2["send()"] --> D1["window.__rstest_dispatch__"]
|
|
42
|
-
R2["dispatchRunnerLifecycle()"] --> D2["window.__rstest_dispatch_rpc__"]
|
|
43
|
-
SN2["snapshot.ts / browserRpc.ts via dispatchTransport.dispatchRpc()"] --> D3["window.__rstest_dispatch_rpc__"]
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
subgraph TopLevelCollectPath["Top-level page path (list collect)"]
|
|
47
|
-
S3["send()"] --> C1["window.__rstest_dispatch__"]
|
|
48
|
-
end
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## Dispatch RPC sequence (snapshot / browser namespaces)
|
|
52
|
-
|
|
53
|
-
The `snapshot` namespace (snapshot file ops) and the `browser` namespace
|
|
54
|
-
(locator/page RPC from `browserRpc.ts`) share one client channel:
|
|
55
|
-
`dispatchTransport.ts` owns request ids, timeouts, and pending-response
|
|
56
|
-
resolution for both the iframe and top-level transports.
|
|
57
|
-
|
|
58
|
-
```mermaid
|
|
59
|
-
sequenceDiagram
|
|
60
|
-
participant Caller as snapshot.ts / browserRpc.ts
|
|
61
|
-
participant Transport as dispatchTransport.ts
|
|
62
|
-
participant Container as browser-ui channel
|
|
63
|
-
participant Host as host dispatch router
|
|
64
|
-
|
|
65
|
-
Caller->>Transport: dispatchRpc(request)
|
|
66
|
-
|
|
67
|
-
alt top-level runner (headless run)
|
|
68
|
-
Transport->>Host: __rstest_dispatch_rpc__(namespace=snapshot|browser)
|
|
69
|
-
Host-->>Transport: BrowserDispatchResponse
|
|
70
|
-
Transport-->>Caller: result/error
|
|
71
|
-
else iframe runner (headed)
|
|
72
|
-
Transport->>Container: postMessage(dispatch-rpc-request)
|
|
73
|
-
Container->>Host: rpc.dispatch(request)
|
|
74
|
-
Host-->>Container: BrowserDispatchResponse
|
|
75
|
-
Container-->>Transport: __rstest_dispatch_response__
|
|
76
|
-
Transport-->>Caller: resolve/reject pending request
|
|
77
|
-
end
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
List collect mode does not use the snapshot or browser RPC namespaces.
|
|
81
|
-
|
|
82
|
-
## Runtime invariants
|
|
83
|
-
|
|
84
|
-
- `entry.ts` is the only bootstrap entry and decides `collect` vs `run` mode.
|
|
85
|
-
- Runner lifecycle events (`file-ready`, `suite-start`, `suite-result`, `case-start`) go through the `runner` dispatch namespace.
|
|
86
|
-
- Snapshot file operations go through the `snapshot` dispatch namespace and never access filesystem directly in browser runtime.
|
|
87
|
-
- Console interception is per test file and must restore original console methods in `finally`.
|
|
88
|
-
- An unhandled window error or `unhandledrejection` that escapes a test file fails the file even when every test passed. Before finalizing each file result, the runner yields two macrotasks so a rejection leaked by a synchronous test is still observed (the browser dispatches `unhandledrejection` in a task queued after the current task).
|
|
89
|
-
- The `getCountOfFailedTests` runner hook returns the client-local per-file failed count; cross-file `bail` is enforced host-side at file boundaries (see `../AGENTS.md`), not by this hook.
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/** Stringify a single console argument for terminal forwarding. */
|
|
2
|
-
export const formatArg = (arg: unknown): string => {
|
|
3
|
-
if (arg === null) return 'null';
|
|
4
|
-
if (arg === undefined) return 'undefined';
|
|
5
|
-
if (typeof arg === 'string') return arg;
|
|
6
|
-
if (typeof arg === 'number' || typeof arg === 'boolean') return String(arg);
|
|
7
|
-
// `JSON.stringify(symbol)` is `undefined`, so a symbol must be handled before
|
|
8
|
-
// the JSON path or it would forward as an empty/`undefined` log.
|
|
9
|
-
if (typeof arg === 'symbol') return arg.toString();
|
|
10
|
-
if (arg instanceof Error) {
|
|
11
|
-
return arg.stack || `${arg.name}: ${arg.message}`;
|
|
12
|
-
}
|
|
13
|
-
try {
|
|
14
|
-
return JSON.stringify(arg, null, 2);
|
|
15
|
-
} catch {
|
|
16
|
-
return String(arg);
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
// Format specifiers understood by the browser console. `%c` applies CSS in a
|
|
21
|
-
// real console; forwarded to a terminal there is no styling, so it is consumed
|
|
22
|
-
// and dropped (matching how Node's `util.format` handles it). `%%` is a literal
|
|
23
|
-
// percent sign. Kept aligned with the browser console spec (no Node-only `%j`,
|
|
24
|
-
// since these logs originate in the browser).
|
|
25
|
-
// cspell:ignore sdifo WHATWG
|
|
26
|
-
const HAS_SPECIFIER = /%[sdifoOc%]/;
|
|
27
|
-
const SPECIFIER = /%[sdifoOc%]/g;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Join console arguments the way a console does. When the first argument is a
|
|
31
|
-
* string carrying `printf`-style specifiers, substitute the following arguments
|
|
32
|
-
* into it (consuming `%c` styles without emitting them), then append any
|
|
33
|
-
* leftover arguments. Otherwise fall back to a plain space-join.
|
|
34
|
-
*
|
|
35
|
-
* Without this, `console.info('%cText', 'font-weight:bold')` (e.g. React's
|
|
36
|
-
* DevTools notice) leaks the raw `%c` directive and its CSS argument into the
|
|
37
|
-
* forwarded terminal output.
|
|
38
|
-
*/
|
|
39
|
-
export const formatConsoleArgs = (args: unknown[]): string => {
|
|
40
|
-
const first = args[0];
|
|
41
|
-
if (typeof first !== 'string' || !HAS_SPECIFIER.test(first)) {
|
|
42
|
-
return args.map(formatArg).join(' ');
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
let next = 1;
|
|
46
|
-
const substituted = first.replace(SPECIFIER, (spec) => {
|
|
47
|
-
if (spec === '%%') return '%';
|
|
48
|
-
if (next >= args.length) return spec;
|
|
49
|
-
const arg = args[next++];
|
|
50
|
-
switch (spec) {
|
|
51
|
-
case '%c':
|
|
52
|
-
return ''; // CSS directive: swallow the style argument
|
|
53
|
-
case '%s':
|
|
54
|
-
return formatArg(arg);
|
|
55
|
-
case '%d':
|
|
56
|
-
case '%i': {
|
|
57
|
-
// Match the browser console formatter (WHATWG): parseInt on the value,
|
|
58
|
-
// so unit-suffixed strings like '42px' format as 42 (not NaN).
|
|
59
|
-
// `String()` first keeps Symbol safe — a bare `parseInt(symbol)` throws
|
|
60
|
-
// via ToString, whereas the spec maps a Symbol to NaN.
|
|
61
|
-
const num = Number.parseInt(String(arg), 10);
|
|
62
|
-
return Number.isNaN(num) ? 'NaN' : String(num);
|
|
63
|
-
}
|
|
64
|
-
case '%f': {
|
|
65
|
-
const num = Number.parseFloat(String(arg));
|
|
66
|
-
return Number.isNaN(num) ? 'NaN' : String(num);
|
|
67
|
-
}
|
|
68
|
-
default: // %o, %O
|
|
69
|
-
return formatArg(arg);
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
if (next >= args.length) {
|
|
74
|
-
return substituted;
|
|
75
|
-
}
|
|
76
|
-
return [substituted, ...args.slice(next).map(formatArg)].join(' ');
|
|
77
|
-
};
|
package/src/client/public.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Re-export runtime API from @rstest/core/internal/browser-runtime for browser use.
|
|
3
|
-
* This file is used as an alias target for '@rstest/core' in browser mode.
|
|
4
|
-
*
|
|
5
|
-
* Uses @rstest/core/internal/browser-runtime which only exports the test APIs
|
|
6
|
-
* (describe, it, expect, etc.) without any Node.js dependencies.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
// Re-export types from @rstest/core (these are compile-time only)
|
|
10
|
-
export type { Assertion, Mock } from '@rstest/core';
|
|
11
|
-
// Re-export all public test APIs
|
|
12
|
-
export * from '@rstest/core/internal/browser-runtime';
|
package/src/watchCliShortcuts.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { color, isTTY, logger } from '@rstest/core/internal/browser';
|
|
2
|
-
|
|
3
|
-
export const isBrowserWatchCliShortcutsEnabled = (): boolean => isTTY('stdin');
|
|
4
|
-
|
|
5
|
-
export const getBrowserWatchCliShortcutsHintMessage = (): string => {
|
|
6
|
-
return ` ${color.dim('press')} ${color.bold('q')} ${color.dim('to quit')}\n`;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
export const logBrowserWatchReadyMessage = (
|
|
10
|
-
enableCliShortcuts: boolean,
|
|
11
|
-
): void => {
|
|
12
|
-
logger.log(color.green(' Waiting for file changes...'));
|
|
13
|
-
|
|
14
|
-
if (enableCliShortcuts) {
|
|
15
|
-
logger.log(getBrowserWatchCliShortcutsHintMessage());
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export async function setupBrowserWatchCliShortcuts({
|
|
20
|
-
close,
|
|
21
|
-
}: {
|
|
22
|
-
close: () => Promise<void>;
|
|
23
|
-
}): Promise<() => void> {
|
|
24
|
-
const { emitKeypressEvents } = await import('node:readline');
|
|
25
|
-
|
|
26
|
-
emitKeypressEvents(process.stdin);
|
|
27
|
-
process.stdin.setRawMode(true);
|
|
28
|
-
process.stdin.resume();
|
|
29
|
-
process.stdin.setEncoding('utf8');
|
|
30
|
-
|
|
31
|
-
let isClosing = false;
|
|
32
|
-
|
|
33
|
-
const handleKeypress = (
|
|
34
|
-
str: string,
|
|
35
|
-
key: { name: string; ctrl: boolean },
|
|
36
|
-
) => {
|
|
37
|
-
if (key.ctrl && key.name === 'c') {
|
|
38
|
-
process.kill(process.pid, 'SIGINT');
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (key.ctrl && key.name === 'z') {
|
|
43
|
-
if (process.platform !== 'win32') {
|
|
44
|
-
process.kill(process.pid, 'SIGTSTP');
|
|
45
|
-
}
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (str !== 'q' || isClosing) {
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// TODO: Support more browser watch shortcuts only after this path is
|
|
54
|
-
// refactored to share the same shortcut model as node mode.
|
|
55
|
-
isClosing = true;
|
|
56
|
-
void (async () => {
|
|
57
|
-
try {
|
|
58
|
-
await close();
|
|
59
|
-
} finally {
|
|
60
|
-
process.exit(0);
|
|
61
|
-
}
|
|
62
|
-
})();
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
process.stdin.on('keypress', handleKeypress);
|
|
66
|
-
|
|
67
|
-
return () => {
|
|
68
|
-
try {
|
|
69
|
-
process.stdin.setRawMode(false);
|
|
70
|
-
process.stdin.pause();
|
|
71
|
-
} catch {
|
|
72
|
-
// do nothing
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
process.stdin.off('keypress', handleKeypress);
|
|
76
|
-
};
|
|
77
|
-
}
|