-
Notifications
You must be signed in to change notification settings - Fork 4k
/
util.ts
112 lines (96 loc) · 3.13 KB
/
util.ts
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import { spawnSync, SpawnSyncOptions } from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
export interface CallSite {
getThis(): any;
getTypeName(): string;
getFunctionName(): string;
getMethodName(): string;
getFileName(): string;
getLineNumber(): number;
getColumnNumber(): number;
getFunction(): Function;
getEvalOrigin(): string;
isNative(): boolean;
isToplevel(): boolean;
isEval(): boolean;
isConstructor(): boolean;
}
/**
* Get callsites from the V8 stack trace API
*
* https://github.com/sindresorhus/callsites
*/
export function callsites(): CallSite[] {
const _prepareStackTrace = Error.prepareStackTrace;
Error.prepareStackTrace = (_, stack) => stack;
const stack = new Error().stack?.slice(1);
Error.prepareStackTrace = _prepareStackTrace;
return stack as unknown as CallSite[];
}
/**
* Find a file by walking up parent directories
*/
export function findUp(name: string, directory: string = process.cwd()): string | undefined {
const absoluteDirectory = path.resolve(directory);
const file = path.join(directory, name);
if (fs.existsSync(file)) {
return file;
}
const { root } = path.parse(absoluteDirectory);
if (absoluteDirectory === root) {
return undefined;
}
return findUp(name, path.dirname(absoluteDirectory));
}
/**
* Spawn sync with error handling
*/
export function exec(cmd: string, args: string[], options?: SpawnSyncOptions) {
const proc = spawnSync(cmd, args, options);
if (proc.error) {
throw proc.error;
}
if (proc.status !== 0) {
if (proc.stdout || proc.stderr) {
throw new Error(`[Status ${proc.status}] stdout: ${proc.stdout?.toString().trim()}\n\n\nstderr: ${proc.stderr?.toString().trim()}`);
}
throw new Error(`${cmd} ${args.join(' ')} ${options.cwd ? `run in directory ${options.cwd}` : ''} exited with status ${proc.status}`);
}
return proc;
}
/**
* Returns a module version by requiring its package.json file
*/
export function tryGetModuleVersion(mod: string): string | undefined {
try {
// eslint-disable-next-line @typescript-eslint/no-require-imports
return require(`${mod}/package.json`).version;
} catch (err) {
return undefined;
}
}
/**
* Extract versions for a list of modules.
*
* First lookup the version in the package.json and then fallback to requiring
* the module's package.json. The fallback is needed for transitive dependencies.
*/
export function extractDependencies(pkgPath: string, modules: string[]): { [key: string]: string } {
const dependencies: { [key: string]: string } = {};
// Use require for cache
const pkgJson = require(pkgPath); // eslint-disable-line @typescript-eslint/no-require-imports
const pkgDependencies = {
...pkgJson.dependencies ?? {},
...pkgJson.devDependencies ?? {},
...pkgJson.peerDependencies ?? {},
};
for (const mod of modules) {
const version = pkgDependencies[mod] ?? tryGetModuleVersion(mod);
if (!version) {
throw new Error(`Cannot extract version for module '${mod}'. Check that it's referenced in your package.json or installed.`);
}
dependencies[mod] = version;
}
return dependencies;
}