@react-native-harness/tools 1.0.0 → 1.1.0-rc.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/dist/__tests__/abort.test.d.ts +2 -0
- package/dist/__tests__/abort.test.d.ts.map +1 -0
- package/dist/__tests__/abort.test.js +49 -0
- package/dist/__tests__/crash-artifacts.test.d.ts +2 -0
- package/dist/__tests__/crash-artifacts.test.d.ts.map +1 -0
- package/dist/__tests__/crash-artifacts.test.js +77 -0
- package/dist/abort.d.ts +2 -0
- package/dist/abort.d.ts.map +1 -1
- package/dist/abort.js +10 -3
- package/dist/crash-artifacts.d.ts +20 -0
- package/dist/crash-artifacts.d.ts.map +1 -0
- package/dist/crash-artifacts.js +57 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/logger.d.ts +12 -7
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +50 -43
- package/dist/regex.d.ts +2 -0
- package/dist/regex.d.ts.map +1 -0
- package/dist/regex.js +1 -0
- package/dist/spawn.d.ts.map +1 -1
- package/dist/spawn.js +35 -16
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/eslint.config.mjs +4 -1
- package/package.json +1 -2
- package/src/__tests__/abort.test.ts +71 -0
- package/src/__tests__/crash-artifacts.test.ts +94 -0
- package/src/abort.ts +15 -3
- package/src/crash-artifacts.ts +140 -0
- package/src/index.ts +3 -1
- package/src/logger.ts +73 -45
- package/src/regex.ts +2 -0
- package/src/spawn.ts +43 -17
- package/tsconfig.lib.json +2 -1
- package/dist/runtime.d.ts +0 -10
- package/dist/runtime.d.ts.map +0 -1
- package/dist/runtime.js +0 -10
- package/dist/timeout.d.ts +0 -2
- package/dist/timeout.d.ts.map +0 -1
- package/dist/timeout.js +0 -16
package/src/logger.ts
CHANGED
|
@@ -1,49 +1,59 @@
|
|
|
1
1
|
import util from 'node:util';
|
|
2
|
-
import { log as clackLog } from '@clack/prompts';
|
|
3
|
-
import isUnicodeSupported from 'is-unicode-supported';
|
|
4
|
-
import { color } from './color.js';
|
|
5
2
|
|
|
6
|
-
|
|
3
|
+
let verbose = !!process.env.HARNESS_DEBUG;
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
type LoggerLevel = 'debug' | 'info' | 'warn' | 'error' | 'log' | 'success';
|
|
6
|
+
type LoggerMethod = (...messages: Array<unknown>) => void;
|
|
10
7
|
|
|
11
|
-
|
|
8
|
+
export type HarnessLogger = {
|
|
9
|
+
debug: LoggerMethod;
|
|
10
|
+
info: LoggerMethod;
|
|
11
|
+
warn: LoggerMethod;
|
|
12
|
+
error: LoggerMethod;
|
|
13
|
+
log: LoggerMethod;
|
|
14
|
+
success: LoggerMethod;
|
|
15
|
+
child: (scope: string) => HarnessLogger;
|
|
16
|
+
setVerbose: (level: boolean) => void;
|
|
17
|
+
isVerbose: () => boolean;
|
|
18
|
+
};
|
|
12
19
|
|
|
13
|
-
|
|
20
|
+
const BASE_TAG = '[harness]';
|
|
14
21
|
|
|
15
|
-
const
|
|
16
|
-
const output = util.format(...messages);
|
|
17
|
-
clackLog.success(output);
|
|
18
|
-
};
|
|
22
|
+
const getTimestamp = (): string => new Date().toISOString();
|
|
19
23
|
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
const normalizeScope = (scope: string): string =>
|
|
25
|
+
scope
|
|
26
|
+
.trim()
|
|
27
|
+
.replace(/^\[+|\]+$/g, '')
|
|
28
|
+
.replace(/\]\[/g, '][');
|
|
24
29
|
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
|
|
30
|
+
const formatPrefix = (scopes: readonly string[]): string => {
|
|
31
|
+
const suffix = scopes.map((scope) => `[${normalizeScope(scope)}]`).join('');
|
|
32
|
+
return `${BASE_TAG}${suffix}`;
|
|
28
33
|
};
|
|
29
34
|
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
35
|
+
const mapLines = (text: string, prefix: string) =>
|
|
36
|
+
text
|
|
37
|
+
.split('\n')
|
|
38
|
+
.map((line) => `${prefix} ${line}`)
|
|
39
|
+
.join('\n');
|
|
34
40
|
|
|
35
|
-
const
|
|
41
|
+
const writeLog = (
|
|
42
|
+
level: LoggerLevel,
|
|
43
|
+
scopes: readonly string[],
|
|
44
|
+
messages: Array<unknown>
|
|
45
|
+
) => {
|
|
46
|
+
const method =
|
|
47
|
+
level === 'warn'
|
|
48
|
+
? console.warn
|
|
49
|
+
: level === 'error'
|
|
50
|
+
? console.error
|
|
51
|
+
: level === 'debug'
|
|
52
|
+
? console.debug
|
|
53
|
+
: console.info;
|
|
36
54
|
const output = util.format(...messages);
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const debug = (...messages: Array<unknown>) => {
|
|
41
|
-
if (verbose) {
|
|
42
|
-
const output = util.format(...messages);
|
|
43
|
-
clackLog.message(mapLines(output, color.dim), {
|
|
44
|
-
symbol: color.dim(SYMBOL_DEBUG),
|
|
45
|
-
});
|
|
46
|
-
}
|
|
55
|
+
const prefix = `${getTimestamp()} ${formatPrefix(scopes)}`;
|
|
56
|
+
method(mapLines(output, prefix));
|
|
47
57
|
};
|
|
48
58
|
|
|
49
59
|
const setVerbose = (level: boolean) => {
|
|
@@ -54,17 +64,35 @@ const isVerbose = () => {
|
|
|
54
64
|
return verbose;
|
|
55
65
|
};
|
|
56
66
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
67
|
+
const createScopedLogger = (scopes: readonly string[] = []): HarnessLogger => ({
|
|
68
|
+
debug: (...messages) => {
|
|
69
|
+
if (!verbose) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
writeLog('debug', scopes, messages);
|
|
74
|
+
},
|
|
75
|
+
info: (...messages) => {
|
|
76
|
+
writeLog('info', scopes, messages);
|
|
77
|
+
},
|
|
78
|
+
warn: (...messages) => {
|
|
79
|
+
writeLog('warn', scopes, messages);
|
|
80
|
+
},
|
|
81
|
+
error: (...messages) => {
|
|
82
|
+
writeLog('error', scopes, messages);
|
|
83
|
+
},
|
|
84
|
+
log: (...messages) => {
|
|
85
|
+
writeLog('log', scopes, messages);
|
|
86
|
+
},
|
|
87
|
+
success: (...messages) => {
|
|
88
|
+
writeLog('success', scopes, messages);
|
|
89
|
+
},
|
|
90
|
+
child: (scope) => createScopedLogger([...scopes, scope]),
|
|
64
91
|
setVerbose,
|
|
65
92
|
isVerbose,
|
|
66
|
-
};
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
export const createLogger = (scope: string): HarnessLogger =>
|
|
96
|
+
createScopedLogger([scope]);
|
|
67
97
|
|
|
68
|
-
|
|
69
|
-
return text.split('\n').map(colorFn).join('\n');
|
|
70
|
-
}
|
|
98
|
+
export const logger = createScopedLogger();
|
package/src/regex.ts
ADDED
package/src/spawn.ts
CHANGED
|
@@ -3,6 +3,7 @@ import nanoSpawn, { SubprocessError } from 'nano-spawn';
|
|
|
3
3
|
import { logger } from './logger.js';
|
|
4
4
|
|
|
5
5
|
export type SpawnOptions = Options;
|
|
6
|
+
const spawnLogger = logger.child('spawn');
|
|
6
7
|
|
|
7
8
|
export const spawn = (
|
|
8
9
|
file: string,
|
|
@@ -16,7 +17,8 @@ export const spawn = (
|
|
|
16
17
|
// Always 'pipe' stderr to handle errors properly down the line
|
|
17
18
|
stderr: 'pipe',
|
|
18
19
|
};
|
|
19
|
-
|
|
20
|
+
const command = [file, ...(args ?? [])].join(' ');
|
|
21
|
+
spawnLogger.debug('running command: %s', command);
|
|
20
22
|
const childProcess = nanoSpawn(file, args, { ...defaultOptions, ...options });
|
|
21
23
|
|
|
22
24
|
setupChildProcessCleanup(childProcess);
|
|
@@ -33,6 +35,43 @@ export const spawnAndForget = async (file: string, args?: readonly string[], opt
|
|
|
33
35
|
|
|
34
36
|
export { Subprocess, SubprocessError };
|
|
35
37
|
|
|
38
|
+
const activeChildProcesses = new Set<Subprocess>();
|
|
39
|
+
let isProcessCleanupInstalled = false;
|
|
40
|
+
|
|
41
|
+
const terminateActiveChildren = async () => {
|
|
42
|
+
const children = [...activeChildProcesses];
|
|
43
|
+
|
|
44
|
+
await Promise.allSettled(
|
|
45
|
+
children.map(async (childProcess) => {
|
|
46
|
+
try {
|
|
47
|
+
(await childProcess.nodeChildProcess).kill();
|
|
48
|
+
} catch {
|
|
49
|
+
// Ignore cleanup failures while shutting down.
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const installProcessCleanup = () => {
|
|
56
|
+
if (isProcessCleanupInstalled) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
isProcessCleanupInstalled = true;
|
|
61
|
+
|
|
62
|
+
const terminate = async () => {
|
|
63
|
+
await terminateActiveChildren();
|
|
64
|
+
process.exit(1);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
process.on('SIGINT', () => {
|
|
68
|
+
void terminate();
|
|
69
|
+
});
|
|
70
|
+
process.on('SIGTERM', () => {
|
|
71
|
+
void terminate();
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
|
|
36
75
|
const setupChildProcessCleanup = (childProcess: Subprocess) => {
|
|
37
76
|
// https://stackoverflow.com/questions/53049939/node-daemon-wont-start-with-process-stdin-setrawmodetrue/53050098#53050098
|
|
38
77
|
if (process.stdin.isTTY) {
|
|
@@ -41,24 +80,11 @@ const setupChildProcessCleanup = (childProcess: Subprocess) => {
|
|
|
41
80
|
process.stdin.setRawMode(false);
|
|
42
81
|
}
|
|
43
82
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
(await childProcess.nodeChildProcess).kill();
|
|
47
|
-
process.exit(1);
|
|
48
|
-
} catch {
|
|
49
|
-
// ignore
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
const sigintHandler = () => terminate();
|
|
54
|
-
const sigtermHandler = () => terminate();
|
|
55
|
-
|
|
56
|
-
process.on('SIGINT', sigintHandler);
|
|
57
|
-
process.on('SIGTERM', sigtermHandler);
|
|
83
|
+
installProcessCleanup();
|
|
84
|
+
activeChildProcesses.add(childProcess);
|
|
58
85
|
|
|
59
86
|
const cleanup = () => {
|
|
60
|
-
|
|
61
|
-
process.off('SIGTERM', sigtermHandler);
|
|
87
|
+
activeChildProcesses.delete(childProcess);
|
|
62
88
|
};
|
|
63
89
|
|
|
64
90
|
childProcess.nodeChildProcess.finally(cleanup);
|
package/tsconfig.lib.json
CHANGED
package/dist/runtime.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
declare global {
|
|
2
|
-
var Bun: boolean | undefined;
|
|
3
|
-
var Deno: boolean | undefined;
|
|
4
|
-
}
|
|
5
|
-
export declare const isBun: boolean;
|
|
6
|
-
export declare const isDeno: boolean;
|
|
7
|
-
export declare const isNode: boolean;
|
|
8
|
-
export type Runtime = 'bun' | 'deno' | 'node';
|
|
9
|
-
export declare const getRuntime: () => Runtime;
|
|
10
|
-
//# sourceMappingURL=runtime.d.ts.map
|
package/dist/runtime.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,CAAC;IACb,IAAI,GAAG,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,IAAI,IAAI,EAAE,OAAO,GAAG,SAAS,CAAC;CAC/B;AAED,eAAO,MAAM,KAAK,EAAE,OAAsB,CAAC;AAC3C,eAAO,MAAM,MAAM,EAAE,OAAuB,CAAC;AAC7C,eAAO,MAAM,MAAM,EAAE,OAAoD,CAAC;AAE1E,MAAM,MAAM,OAAO,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAE9C,eAAO,MAAM,UAAU,QAAO,OAI7B,CAAC"}
|
package/dist/runtime.js
DELETED
package/dist/timeout.d.ts
DELETED
package/dist/timeout.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"timeout.d.ts","sourceRoot":"","sources":["../src/timeout.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,GAAI,CAAC,EAC3B,SAAS,OAAO,CAAC,CAAC,CAAC,EACnB,SAAS,MAAM,EACf,WAAW,MAAM,KAAK,KACrB,OAAO,CAAC,CAAC,CAkBX,CAAC"}
|
package/dist/timeout.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export const withTimeout = (promise, timeout, onTimeout) => {
|
|
2
|
-
return new Promise((resolve, reject) => {
|
|
3
|
-
const timeoutId = setTimeout(() => {
|
|
4
|
-
reject(onTimeout());
|
|
5
|
-
}, timeout);
|
|
6
|
-
const onResolve = (result) => {
|
|
7
|
-
clearTimeout(timeoutId);
|
|
8
|
-
resolve(result);
|
|
9
|
-
};
|
|
10
|
-
const onReject = (error) => {
|
|
11
|
-
clearTimeout(timeoutId);
|
|
12
|
-
reject(error);
|
|
13
|
-
};
|
|
14
|
-
promise.then(onResolve).catch(onReject);
|
|
15
|
-
});
|
|
16
|
-
};
|