-
-
Notifications
You must be signed in to change notification settings - Fork 549
Expand file tree
/
Copy pathctx-ts-node.ts
More file actions
93 lines (88 loc) · 2.9 KB
/
ctx-ts-node.ts
File metadata and controls
93 lines (88 loc) · 2.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import { exec as childProcessExec } from 'child_process';
import { lock } from 'proper-lockfile';
import { promisify } from 'util';
import { existsSync, readFileSync, writeFileSync } from 'fs';
import { join } from 'path';
import type { ExecutionContext } from '../testlib';
import { sync as rimrafSync } from 'rimraf';
import { TEST_DIR } from './paths';
import { testsDirRequire, tsNodeBinTypes, tsNodeTypes } from './misc';
/** Pass to `test.context()` to get access to the ts-node API under test */
export async function ctxTsNode() {
await installTsNode();
const tsNodeUnderTest: typeof tsNodeTypes = testsDirRequire('ts-node');
const tsNodeBin: typeof tsNodeBinTypes = testsDirRequire('ts-node/dist/bin');
return {
tsNodeUnderTest,
tsNodeBin,
};
}
export namespace ctxTsNode {
export type Ctx = Awaited<ReturnType<typeof ctxTsNode>>;
export type T = ExecutionContext<Ctx>;
}
const ts_node_install_lock = process.env.ts_node_install_lock as string;
const lockPath = join(__dirname, ts_node_install_lock);
interface InstallationResult {
error: string | null;
}
/**
* Pack and install ts-node locally, necessary to test package "exports"
* FS locking b/c tests run in separate processes
*/
export async function installTsNode() {
await lockedMemoizedOperation(lockPath, async () => {
const totalTries = process.platform === 'win32' ? 5 : 1;
let tries = 0;
while (true) {
try {
rimrafSync(join(TEST_DIR, '.yarn/cache/ts-node-file-*'));
writeFileSync(join(TEST_DIR, 'yarn.lock'), '');
const result = await promisify(childProcessExec)(`yarn --no-immutable`, {
cwd: TEST_DIR,
});
// You can uncomment this to aid debugging
// console.log(result.stdout, result.stderr);
rimrafSync(join(TEST_DIR, '.yarn/cache/ts-node-file-*'));
writeFileSync(join(TEST_DIR, 'yarn.lock'), '');
break;
} catch (e) {
tries++;
if (tries >= totalTries) throw e;
}
}
});
}
/**
* Attempt an operation once across multiple processes, using filesystem locking.
* If it was executed already by another process, and it errored, throw the same error message.
*/
async function lockedMemoizedOperation(lockPath: string, operation: () => Promise<void>) {
const releaseLock = await lock(lockPath, {
realpath: false,
stale: 120e3,
retries: {
retries: 120,
maxTimeout: 1000,
},
});
try {
const operationHappened = existsSync(lockPath);
if (operationHappened) {
const result: InstallationResult = JSON.parse(readFileSync(lockPath, 'utf8'));
if (result.error) throw result.error;
} else {
const result: InstallationResult = { error: null };
try {
await operation();
} catch (e) {
result.error = `${e}`;
throw e;
} finally {
writeFileSync(lockPath, JSON.stringify(result));
}
}
} finally {
releaseLock();
}
}