8
import watcher from '@parcel/watcher';
9
import path from 'path';
10
import ts from 'typescript';
11
-import {FILTER_FILENAME, FIXTURES_PATH, PROJECT_ROOT} from './constants';
12
-import {TestFilter, readTestFilter} from './fixture-utils';
11
+import {FIXTURES_PATH, PROJECT_ROOT} from './constants';
12
+import {TestFilter} from './fixture-utils';
13
import {execSync} from 'child_process';
14
15
export function watchSrc(
117
lastUpdate: number;
118
mode: RunnerMode;
119
filter: TestFilter | null;
120
+ debug: boolean;
121
+ // Input mode for interactive pattern entry
122
+ inputMode: 'none' | 'pattern';
123
+ inputBuffer: string;
124
};
125
126
function subscribeFixtures(
146
});
147
}
148
145
-function subscribeFilterFile(
146
- state: RunnerState,
147
- onChange: (state: RunnerState) => void,
148
-) {
149
- watcher.subscribe(PROJECT_ROOT, async (err, events) => {
150
- if (err) {
151
- console.error(err);
152
- process.exit(1);
153
- } else if (
154
- events.findIndex(event => event.path.includes(FILTER_FILENAME)) !== -1
155
- ) {
156
- if (state.mode.filter) {
157
- state.filter = await readTestFilter();
158
- state.mode.action = RunnerAction.Test;
159
- onChange(state);
160
- }
161
- }
162
- });
163
-}
164
-
149
function subscribeTsc(
150
state: RunnerState,
151
onChange: (state: RunnerState) => void,
184
onChange: (state: RunnerState) => void,
185
) {
186
process.stdin.on('keypress', async (str, key) => {
187
+ // Handle input mode (pattern entry)
188
+ if (state.inputMode !== 'none') {
189
+ if (key.name === 'return') {
190
+ // Enter pressed - process input
191
+ const pattern = state.inputBuffer.trim();
192
+ state.inputMode = 'none';
193
+ state.inputBuffer = '';
194
+ process.stdout.write('\n');
195
+
196
+ if (pattern !== '') {
197
+ // Set the pattern as filter
198
+ state.filter = {paths: [pattern]};
199
+ state.mode.filter = true;
200
+ state.mode.action = RunnerAction.Test;
201
+ onChange(state);
202
+ }
203
+ // If empty, just exit input mode without changes
204
+ return;
205
+ } else if (key.name === 'escape') {
206
+ // Cancel input mode
207
+ state.inputMode = 'none';
208
+ state.inputBuffer = '';
209
+ process.stdout.write(' (cancelled)\n');
210
+ return;
211
+ } else if (key.name === 'backspace') {
212
+ if (state.inputBuffer.length > 0) {
213
+ state.inputBuffer = state.inputBuffer.slice(0, -1);
214
+ // Erase character: backspace, space, backspace
215
+ process.stdout.write('\b \b');
216
+ }
217
+ return;
218
+ } else if (str && !key.ctrl && !key.meta) {
219
+ // Regular character - accumulate and echo
220
+ state.inputBuffer += str;
221
+ process.stdout.write(str);
222
+ return;
223
+ }
224
+ return; // Ignore other keys in input mode
225
+ }
226
+
227
+ // Normal mode keypress handling
228
if (key.name === 'u') {
229
// u => update fixtures
230
state.mode.action = RunnerAction.Update;
231
} else if (key.name === 'q') {
232
process.exit(0);
208
- } else if (key.name === 'f') {
209
- state.mode.filter = !state.mode.filter;
210
- state.filter = state.mode.filter ? await readTestFilter() : null;
233
+ } else if (key.name === 'a') {
234
+ // a => exit filter mode and run all tests
235
+ state.mode.filter = false;
236
+ state.filter = null;
237
+ state.mode.action = RunnerAction.Test;
238
+ } else if (key.name === 'd') {
239
+ // d => toggle debug logging
240
+ state.debug = !state.debug;
241
state.mode.action = RunnerAction.Test;
242
+ } else if (key.name === 'p') {
243
+ // p => enter pattern input mode
244
+ state.inputMode = 'pattern';
245
+ state.inputBuffer = '';
246
+ process.stdout.write('Pattern: ');
247
+ return; // Don't trigger onChange yet
248
} else {
249
// any other key re-runs tests
250
state.mode.action = RunnerAction.Test;
255
256
export async function makeWatchRunner(
257
onChange: (state: RunnerState) => void,
222
- filterMode: boolean,
258
+ debugMode: boolean,
259
+ initialPattern?: string,
260
): Promise<void> {
224
- const state = {
261
+ // Determine initial filter state
262
+ let filter: TestFilter | null = null;
263
+ let filterEnabled = false;
264
+
265
+ if (initialPattern) {
266
+ filter = {paths: [initialPattern]};
267
+ filterEnabled = true;
268
+ }
269
+
270
+ const state: RunnerState = {
271
compilerVersion: 0,
272
isCompilerBuildValid: false,
273
lastUpdate: -1,
274
mode: {
275
action: RunnerAction.Test,
230
- filter: filterMode,
276
+ filter: filterEnabled,
277
},
232
- filter: filterMode ? await readTestFilter() : null,
278
+ filter,
279
+ debug: debugMode,
280
+ inputMode: 'none',
281
+ inputBuffer: '',
282
};
283
284
subscribeTsc(state, onChange);
285
subscribeFixtures(state, onChange);
286
subscribeKeyEvents(state, onChange);
238
- subscribeFilterFile(state, onChange);
287
}