@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.
Files changed (41) hide show
  1. package/dist/__tests__/abort.test.d.ts +2 -0
  2. package/dist/__tests__/abort.test.d.ts.map +1 -0
  3. package/dist/__tests__/abort.test.js +49 -0
  4. package/dist/__tests__/crash-artifacts.test.d.ts +2 -0
  5. package/dist/__tests__/crash-artifacts.test.d.ts.map +1 -0
  6. package/dist/__tests__/crash-artifacts.test.js +77 -0
  7. package/dist/abort.d.ts +2 -0
  8. package/dist/abort.d.ts.map +1 -1
  9. package/dist/abort.js +10 -3
  10. package/dist/crash-artifacts.d.ts +20 -0
  11. package/dist/crash-artifacts.d.ts.map +1 -0
  12. package/dist/crash-artifacts.js +57 -0
  13. package/dist/index.d.ts +2 -0
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +2 -0
  16. package/dist/logger.d.ts +12 -7
  17. package/dist/logger.d.ts.map +1 -1
  18. package/dist/logger.js +50 -43
  19. package/dist/regex.d.ts +2 -0
  20. package/dist/regex.d.ts.map +1 -0
  21. package/dist/regex.js +1 -0
  22. package/dist/spawn.d.ts.map +1 -1
  23. package/dist/spawn.js +35 -16
  24. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  25. package/eslint.config.mjs +4 -1
  26. package/package.json +1 -2
  27. package/src/__tests__/abort.test.ts +71 -0
  28. package/src/__tests__/crash-artifacts.test.ts +94 -0
  29. package/src/abort.ts +15 -3
  30. package/src/crash-artifacts.ts +140 -0
  31. package/src/index.ts +3 -1
  32. package/src/logger.ts +73 -45
  33. package/src/regex.ts +2 -0
  34. package/src/spawn.ts +43 -17
  35. package/tsconfig.lib.json +2 -1
  36. package/dist/runtime.d.ts +0 -10
  37. package/dist/runtime.d.ts.map +0 -1
  38. package/dist/runtime.js +0 -10
  39. package/dist/timeout.d.ts +0 -2
  40. package/dist/timeout.d.ts.map +0 -1
  41. 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
- const unicode = isUnicodeSupported();
3
+ let verbose = !!process.env.HARNESS_DEBUG;
7
4
 
8
- const unicodeWithFallback = (c: string, fallback: string) =>
9
- unicode ? c : fallback;
5
+ type LoggerLevel = 'debug' | 'info' | 'warn' | 'error' | 'log' | 'success';
6
+ type LoggerMethod = (...messages: Array<unknown>) => void;
10
7
 
11
- const SYMBOL_DEBUG = unicodeWithFallback('●', '•');
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
- let verbose = !!process.env.HARNESS_DEBUG;
20
+ const BASE_TAG = '[harness]';
14
21
 
15
- const success = (...messages: Array<unknown>) => {
16
- const output = util.format(...messages);
17
- clackLog.success(output);
18
- };
22
+ const getTimestamp = (): string => new Date().toISOString();
19
23
 
20
- const info = (...messages: Array<unknown>) => {
21
- const output = util.format(...messages);
22
- clackLog.info(output);
23
- };
24
+ const normalizeScope = (scope: string): string =>
25
+ scope
26
+ .trim()
27
+ .replace(/^\[+|\]+$/g, '')
28
+ .replace(/\]\[/g, '][');
24
29
 
25
- const warn = (...messages: Array<unknown>) => {
26
- const output = util.format(...messages);
27
- clackLog.warn(mapLines(output, color.yellow));
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 error = (...messages: Array<unknown>) => {
31
- const output = util.format(...messages);
32
- clackLog.error(mapLines(output, color.red));
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 log = (...messages: Array<unknown>) => {
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
- clackLog.step(output);
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
- export const logger = {
58
- success,
59
- info,
60
- warn,
61
- error,
62
- debug,
63
- log,
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
- function mapLines(text: string, colorFn: (line: string) => string) {
69
- return text.split('\n').map(colorFn).join('\n');
70
- }
98
+ export const logger = createScopedLogger();
package/src/regex.ts ADDED
@@ -0,0 +1,2 @@
1
+ export const escapeRegExp = (value: string) =>
2
+ value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
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
- logger.debug(`Running: ${file}`, ...(args ?? []));
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
- const terminate = async () => {
45
- try {
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
- process.off('SIGINT', sigintHandler);
61
- process.off('SIGTERM', sigtermHandler);
87
+ activeChildProcesses.delete(childProcess);
62
88
  };
63
89
 
64
90
  childProcess.nodeChildProcess.finally(cleanup);
package/tsconfig.lib.json CHANGED
@@ -7,7 +7,8 @@
7
7
  "tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo",
8
8
  "emitDeclarationOnly": false,
9
9
  "forceConsistentCasingInFileNames": true,
10
- "types": ["node"]
10
+ "types": ["node"],
11
+ "lib": ["DOM", "ES2022"]
11
12
  },
12
13
  "include": ["src/**/*.ts"],
13
14
  "references": []
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
@@ -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
@@ -1,10 +0,0 @@
1
- export const isBun = !!global.Bun;
2
- export const isDeno = !!global.Deno;
3
- export const isNode = !!global.process?.versions?.node && !isBun;
4
- export const getRuntime = () => {
5
- if (isBun)
6
- return 'bun';
7
- if (isDeno)
8
- return 'deno';
9
- return 'node';
10
- };
package/dist/timeout.d.ts DELETED
@@ -1,2 +0,0 @@
1
- export declare const withTimeout: <T>(promise: Promise<T>, timeout: number, onTimeout: () => Error) => Promise<T>;
2
- //# sourceMappingURL=timeout.d.ts.map
@@ -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
- };