-
-
Notifications
You must be signed in to change notification settings - Fork 143
/
Copy pathutils.js
282 lines (253 loc) · 9.48 KB
/
utils.js
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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
const NodeConnector = require("./node-connector");
const { exec, execFile } = require('child_process');
const fs = require('fs');
const fsPromise = require('fs').promises;
const path = require('path');
const os = require('os');
const {lintFile} = require("./ESLint/service");
let openModule, open; // dynamic import when needed
async function _importOpen() {
if(open){
return open;
}
openModule = await import('open');
open = openModule.default;
}
const UTILS_NODE_CONNECTOR = "ph_utils";
NodeConnector.createNodeConnector(UTILS_NODE_CONNECTOR, exports);
async function getURLContent({url, options}) {
options = options || {
redirect: "follow",
headers: {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36",
"Cache-Control": "no-cache"
}
};
const fetchResponse = await fetch(url, options);
const bufferContents = await fetchResponse.arrayBuffer();
return {
buffer: bufferContents
};
}
async function setLocaleStrings(localStrings) {
exports.Strings = localStrings;
}
/**
* retrieves the phoenix binary version
* @param phoenixBinPath
*/
async function getPhoenixBinaryVersion(phoenixBinPath) {
return new Promise((resolve, reject)=>{
exec(`"${phoenixBinPath}" -v`, (error, stdout, stderr) => {
if (error || stderr) {
reject(`exec error: ${error||stderr}`);
return;
}
resolve(stdout.trim());
});
});
}
async function getLinuxOSFlavorName() {
const osReleaseFile = '/etc/os-release';
try {
const data = fs.readFileSync(osReleaseFile, 'utf8');
const lines = data.split('\n');
const osInfo = {};
lines.forEach(line => {
const [key, value] = line.split('=');
osInfo[key.trim()] = value ? value.replace(/"/g, '') : '';
});
return osInfo.PRETTY_NAME;
} catch (err) {
console.error(`Error reading Linux OS Name ${osReleaseFile}: ${err.message}`);
return null;
}
}
const ALLOWED_BROWSERS_NAMES = [`chrome`, `firefox`, `safari`, `edge`, `browser`, `browserPrivate`];
/**
* Allows opening the given url in one of the supported browsers.
* @param url
* @param {string} browserName one of `chrome`, `firefox`, `safari`, `edge`, `browser`, `browserPrivate`
* @return {Promise<void>}
*/
async function openUrlInBrowser({url, browserName}) {
if(!ALLOWED_BROWSERS_NAMES.includes(browserName)){
throw new Error("openUrlInBrowser: unsupported browser "+browserName+" allowed: "+ALLOWED_BROWSERS_NAMES);
}
await _importOpen();
const appName = browserName === "safari"? "safari":openModule.apps[browserName];
await open(url, {
app: {
name: appName
}
});
}
/**
* Loads a node extension module asynchronously.
*
* @param {string} moduleNativeDir - The path to the node extension module.
* @return {Promise<void>} - A promise that resolves when the module has been loaded.
* @private
*/
async function _loadNodeExtensionModule({moduleNativeDir}) {
require(moduleNativeDir);
}
/**
* Installs npm modules in the specified folder.
*
* @param {string} moduleNativeDir - The directory where the npm modules will be installed.
* @return {Promise<void>} - A Promise that resolves with no value when the installation is complete.
* @private
*/
async function _npmInstallInFolder({moduleNativeDir}) {
const phnodeExePath = process.argv[0];
const npmPath = path.resolve(path.dirname(require.resolve("npm")), "bin", "npm-cli.js");
console.log("npm path", npmPath, "phnode path", phnodeExePath);
// Check if the package.json file exists in the moduleNativeDir
// Check if the package.json file exists in the moduleNativeDir
const packageJsonPath = path.join(moduleNativeDir, 'package.json');
await fsPromise.access(packageJsonPath); // Throws if package.json doesn't exist
// Check if package-lock.json exists in the moduleNativeDir
const packageLockJsonPath = path.join(moduleNativeDir, 'package-lock.json');
let packageLockJsonExists = false;
try {
await fsPromise.access(packageLockJsonPath);
packageLockJsonExists = true;
} catch (error) {
console.log("package-lock.json does not exist, it is recommended to check in package-lock.json," +
" using npm install instead of npm ci", packageLockJsonPath);
}
const npmInstallMode = packageLockJsonExists ? 'ci' : 'install';
const nodeArgs = [npmPath, npmInstallMode, moduleNativeDir];
return new Promise((resolve, reject) => {
console.log(`Running "${phnodeExePath} ${nodeArgs}" in ${moduleNativeDir}`);
execFile(phnodeExePath, nodeArgs, { cwd: moduleNativeDir }, (error) => {
if (error) {
console.error('Error:', error);
reject(error);
} else {
resolve();
console.log(`Successfully ran "${nodeArgs}" in ${moduleNativeDir}`);
}
});
});
}
/**
* If it's a dir that exists, returns that
* If it's a file, it returns the parent directory if it exists
* If no parent exists, it returns the original path.
*
* @param {string} cwd - The path to validate.
* @returns {string} - An existing directory or the original path.
*/
function _getValidDirectory(cwd) {
let currentPath = path.resolve(cwd);
const exists = fs.existsSync(currentPath);
if (exists) {
const isPathDir = fs.statSync(currentPath).isDirectory();
if(isPathDir){
return currentPath;
}
return path.dirname(currentPath);
}
currentPath = path.dirname(currentPath);
if(fs.existsSync(currentPath)){
return currentPath;
}
// If no valid directory is found, fallback to the original cwd
return cwd;
}
/**
* Opens a native terminal window with the specified current working directory.
* Returns a Promise that resolves if the terminal starts successfully, or rejects if it fails.
*
* @param {string} cwd - The directory to open the terminal in.
* @param {boolean} usePowerShell - Whether to use PowerShell instead of cmd on Windows.
* @returns {Promise<void>} - Resolves if the terminal starts, rejects otherwise.
*/
function openNativeTerminal({cwd, usePowerShell = false}) {
return new Promise((resolve, reject) => {
const platform = os.platform();
cwd = _getValidDirectory(cwd);
let command;
if (platform === 'win32') {
if (usePowerShell) {
command = `start powershell -NoExit -Command "Set-Location -Path '${cwd}'"`;
} else {
command = `start cmd /K "cd /D ${cwd}"`;
}
} else if (platform === 'darwin') {
command = `open -a Terminal "${cwd}"`;
} else {
command = `
if command -v gnome-terminal > /dev/null 2>&1; then
gnome-terminal --working-directory="${cwd}";
elif command -v konsole > /dev/null 2>&1; then
konsole --workdir "${cwd}";
elif command -v xfce4-terminal > /dev/null 2>&1; then
xfce4-terminal --working-directory="${cwd}";
elif command -v xterm > /dev/null 2>&1; then
xterm -e "cd '${cwd}' && bash";
else
echo "No supported terminal emulator found.";
exit 1;
fi
`;
}
// Execute the terminal command
exec(command, (error) => {
if (error) {
reject(new Error(`Failed to start terminal: ${error.message}`));
} else {
resolve();
}
});
});
}
/**
* Opens a file in the default application for its type on Windows, macOS, and Linux.
*
* @param {string} fullPath - The path to the file/folder to open.
* @returns {Promise<void>} - Resolves if the file/folder is opened successfully, rejects otherwise.
*/
function openInDefaultApp(fullPath) {
return new Promise((resolve, reject) => {
const platform = os.platform();
let command;
if (platform === 'win32') {
// Windows: Use 'start' command
command = `start "" "${fullPath}"`;
} else if (platform === 'darwin') {
// macOS: Use 'open' command
command = `open "${fullPath}"`;
} else {
// Linux: Use 'xdg-open' command
command = `xdg-open "${fullPath}"`;
}
// Execute the command
exec(command, (error) => {
if (error) {
reject(new Error(`Failed to open file: ${error.message}`));
} else {
resolve();
}
});
});
}
async function ESLintFile({text, fullFilePath, projectFullPath}) {
return lintFile(text, fullFilePath, projectFullPath);
}
async function getEnvironmentVariable(varName) {
return process.env[varName];
}
exports.getURLContent = getURLContent;
exports.setLocaleStrings = setLocaleStrings;
exports.getPhoenixBinaryVersion = getPhoenixBinaryVersion;
exports.getLinuxOSFlavorName = getLinuxOSFlavorName;
exports.openUrlInBrowser = openUrlInBrowser;
exports.getEnvironmentVariable = getEnvironmentVariable;
exports.ESLintFile = ESLintFile;
exports.openNativeTerminal = openNativeTerminal;
exports.openInDefaultApp = openInDefaultApp;
exports._loadNodeExtensionModule = _loadNodeExtensionModule;
exports._npmInstallInFolder = _npmInstallInFolder;