forked from rescript-lang/rescript-vscode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfindBinary.ts
More file actions
165 lines (148 loc) · 5 KB
/
findBinary.ts
File metadata and controls
165 lines (148 loc) · 5 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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
import * as fs from "fs";
import * as fsAsync from "fs/promises";
import * as path from "path";
import * as semver from "semver";
import * as url from "url";
export type BinaryName =
| "bsc.exe"
| "rescript-editor-analysis.exe"
| "rescript-tools.exe"
| "rescript"
| "rewatch.exe"
| "rescript.exe";
type FindBinaryOptions = {
projectRootPath: string | null;
binary: BinaryName;
platformPath?: string | null;
};
const compilerInfoPartialPath = path.join("lib", "bs", "compiler-info.json");
// For arm64, try the arm64-specific directory first (e.g., darwinarm64),
// then fall back to the generic platform directory (e.g., darwin) for older ReScript versions
const platformDirArm64 =
process.arch === "arm64" ? process.platform + process.arch : null;
const platformDirGeneric = process.platform;
const normalizePath = (filePath: string | null): string | null => {
return filePath != null ? path.normalize(filePath) : null;
};
const findFilePathFromProjectRoot = (
directory: string | null,
filePartialPath: string,
): string | null => {
if (directory == null) {
return null;
}
const filePath = path.join(directory, filePartialPath);
if (fs.existsSync(filePath)) {
return normalizePath(filePath);
}
const parentDirStr = path.dirname(directory);
if (parentDirStr === directory) {
return null;
}
return findFilePathFromProjectRoot(
normalizePath(parentDirStr),
filePartialPath,
);
};
export const findBinary = async ({
projectRootPath,
binary,
platformPath,
}: FindBinaryOptions): Promise<string | null> => {
if (platformPath != null) {
const result = path.join(platformPath, binary);
return normalizePath(result);
}
if (projectRootPath !== null) {
try {
const compilerInfo = path.resolve(
projectRootPath,
compilerInfoPartialPath,
);
const contents = await fsAsync.readFile(compilerInfo, "utf8");
const compileInfo = JSON.parse(contents);
if (compileInfo && compileInfo.bsc_path) {
const bscPath = compileInfo.bsc_path;
if (binary === "bsc.exe") {
return normalizePath(bscPath);
} else if (binary !== "rescript") {
// For native binaries (not "rescript" JS wrapper), use the bsc_path directory
const binaryPath = path.join(path.dirname(bscPath), binary);
return normalizePath(binaryPath);
}
// For "rescript", fall through to find the JS wrapper below
}
} catch {}
}
const rescriptDir = findFilePathFromProjectRoot(
projectRootPath,
path.join("node_modules", "rescript"),
);
if (rescriptDir == null) {
return null;
}
let rescriptVersion = null;
let rescriptJSWrapperPath = null;
try {
const rescriptPackageJSONPath = path.join(rescriptDir, "package.json");
const rescriptPackageJSON = JSON.parse(
await fsAsync.readFile(rescriptPackageJSONPath, "utf-8"),
);
rescriptVersion = rescriptPackageJSON.version;
rescriptJSWrapperPath = rescriptPackageJSON.bin.rescript;
} catch {
return null;
}
let binaryPath: string | null = null;
if (binary === "rescript") {
binaryPath = path.join(rescriptDir, rescriptJSWrapperPath);
} else if (semver.gte(rescriptVersion, "12.0.0-alpha.13")) {
const target = `${process.platform}-${process.arch}`;
const targetPackagePath = path.join(
fs.realpathSync(rescriptDir),
"..",
`@rescript/${target}/bin.js`,
);
const { binPaths } = await import(url.fileURLToPath(targetPackagePath));
if (binary === "bsc.exe") {
binaryPath = binPaths.bsc_exe;
} else if (binary === "rescript-editor-analysis.exe") {
binaryPath = binPaths.rescript_editor_analysis_exe;
} else if (binary === "rewatch.exe") {
binaryPath = binPaths.rewatch_exe;
} else if (binary === "rescript.exe") {
binaryPath = binPaths.rescript_exe;
}
} else {
// For older ReScript versions (< 12.0.0-alpha.13), try arm64-specific directory first,
// then fall back to generic platform directory (older versions don't have arm64 directories)
if (platformDirArm64 != null) {
const arm64Path = path.join(rescriptDir, platformDirArm64, binary);
if (fs.existsSync(arm64Path)) {
binaryPath = arm64Path;
}
}
if (binaryPath == null) {
binaryPath = path.join(rescriptDir, platformDirGeneric, binary);
}
}
if (binaryPath != null && fs.existsSync(binaryPath)) {
return normalizePath(binaryPath);
}
return null;
};
/**
* Derives the monorepo root directory from a binary path.
* For a path like `/monorepo/node_modules/.bin/rescript`, returns `/monorepo`.
* This is useful for monorepo support where the binary is in the monorepo root's
* node_modules, but the project root (nearest rescript.json) might be a subpackage.
*/
export const getMonorepoRootFromBinaryPath = (
binaryPath: string | null,
): string | null => {
if (binaryPath == null) {
return null;
}
const match = binaryPath.match(/^(.*?)[\\/]+node_modules[\\/]+/);
return match ? normalizePath(match[1]) : null;
};