package com.wix.nodejs;
import com.google.common.base.Joiner;
import com.intellij.execution.configurations.PathEnvironmentVariableUtil;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.EnvironmentUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.FilenameFilter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class NodeFinder {
// public static final String RT_BASE_NAME = SystemInfo.isWindows ? "rt.cmd" : "rt";
private static final Pattern NVM_NODE_DIR_NAME_PATTERN = Pattern.compile("^v?(\\d+)\\.(\\d+)\\.(\\d+)$");
public static final String NODE_MODULES = "node_modules";
// TODO figure out a way to automatically get this path or add it to config
// should read from /usr/local/lib/node_modules/eslint/lib/rules
// public static String defaultPath = "/usr/local/lib/node_modules/eslint/lib/rules";
// c:/users/user/appdata/roaming/npm/node_modules
private NodeFinder() {
}
public static String getBinName(String baseBinName) {
return SystemInfo.isWindows ? baseBinName + ".cmd" : baseBinName;
}
// List infos = ContainerUtil.newArrayList();
// NodeModuleSearchUtil.findModulesWithName(infos, "eslint", project.getBaseDir(), null, false);
// @Nullable
// public static File findInterpreterInPath() {
// return PathEnvironmentVariableUtil.findInPath(RT_BASE_NAME);
// }
@NotNull
public static List<File> searchNodeModulesBin(String exeFileName) {
Set<File> interpreters = ContainerUtil.newLinkedHashSet();
List<File> fromPath = PathEnvironmentVariableUtil.findAllExeFilesInPath(exeFileName);
List<File> nvmInterpreters = listNodeInterpretersFromNvm(exeFileName);
List<File> brewInterpreters = listNodeInterpretersFromHomeBrew(exeFileName);
interpreters.addAll(fromPath);
interpreters.removeAll(nvmInterpreters);
interpreters.removeAll(brewInterpreters);
interpreters.addAll(nvmInterpreters);
interpreters.addAll(brewInterpreters);
return ContainerUtil.newArrayList(interpreters);
}
@NotNull
public static List<File> searchAllScopesForBin(File projectRoot, String exeFileName) {
// List<File> nodeModules = searchProjectNodeModules(projectRoot);
List<File> globalJscsBin = searchNodeModulesBin(exeFileName);
File file = resolvePath(projectRoot, NODE_MODULES, ".bin", exeFileName);
if (file.exists()) {
globalJscsBin.add(file);
}
// if (SystemInfo.isWindows) {
// File file = resolvePath(projectRoot, NODE_MODULES, ".bin", exeFileName);
// if (file.exists()) {
// globalJscsBin.add(file);
// }
// } else {
// File file = resolvePath(projectRoot, NODE_MODULES, ".bin", exeFileName);
// if (file.exists()) {
// globalJscsBin.add(file);
// }
// }
// globalJscsBin.addAll(nodeModules);
return globalJscsBin;
}
// @NotNull
// public static List<File> searchForRTBin(File projectRoot) {
//// List<File> nodeModules = searchProjectNodeModules(projectRoot);
// List<File> globalRTBin = listPossibleRTExe(exeFileName);
// if (SystemInfo.isWindows) {
// File file = resolvePath(projectRoot, NODE_MODULES, ".bin", "rt.cmd");
// if (file.exists()) {
// globalRTBin.add(file);
// }
// } else {
// File file = resolvePath(projectRoot, NODE_MODULES, "react-templates", "bin", "rt.js");
// if (file.exists()) {
// globalRTBin.add(file);
// }
// }
//// globalRTBin.addAll(nodeModules);
// return globalRTBin;
// }
public static File resolvePath(File root, @Nullable String first, @Nullable String second, String... rest) {
String path = buildPath(first, second, rest);
return new File(root, path);
}
public static String buildPath(@Nullable String first, @Nullable String second, String... rest) {
return Joiner.on(File.separatorChar).join(first, second, (Object[]) rest);
}
@NotNull
public static List<File> listNodeInterpretersFromNvm(String exeFileName) {
String nvmDirPath = EnvironmentUtil.getValue("NVM_DIR");
if (StringUtil.isEmpty(nvmDirPath)) {
return Collections.emptyList();
}
File nvmDir = new File(nvmDirPath);
if (nvmDir.isDirectory() && nvmDir.isAbsolute()) {
return listNodeInterpretersFromVersionDir(nvmDir, exeFileName);
}
return Collections.emptyList();
}
public static List<File> listNodeInterpretersFromHomeBrew(String exeFileName) {
return listNodeInterpretersFromVersionDir(new File("/usr/local/Cellar/node"), exeFileName);
}
public static List<File> listNodeInterpretersFromVersionDir(@NotNull File parentDir, String exeFileName) {
if (!parentDir.isDirectory()) {
return Collections.emptyList();
}
File[] dirs = parentDir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return NodeFinder.structureNodeVersionStr(name) != null;
}
});
if (dirs == null || dirs.length == 0) {
return Collections.emptyList();
}
Arrays.sort(dirs, new Comparator<File>() {
public int compare(File dir1, File dir2) {
int[] v1 = NodeFinder.structureNodeVersionStr(dir1.getName());
int[] v2 = NodeFinder.structureNodeVersionStr(dir2.getName());
if (v1 != null && v2 != null) {
for (int i = 0; i < v1.length; i++) {
if (i < v2.length) {
int cmp = v2[i] - v1[i];
if (cmp != 0) {
return cmp;
}
}
}
}
return dir1.getName().compareTo(dir2.getName());
}
});
List<File> interpreters = ContainerUtil.newArrayListWithCapacity(dirs.length);
for (File dir : dirs) {
File interpreter = new File(dir, "bin" + File.separator + exeFileName);
if (interpreter.isFile() && interpreter.canExecute()) {
interpreters.add(interpreter);
}
}
return interpreters;
}
@Nullable
private static int[] structureNodeVersionStr(@NotNull String name) {
Matcher matcher = NVM_NODE_DIR_NAME_PATTERN.matcher(name);
if (matcher.matches() && matcher.groupCount() == 3) {
try {
return new int[]{Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)), Integer.parseInt(matcher.group(3))};
} catch (Exception ignored) {
}
}
return null;
}
// public static File find(File projectRoot, String exe) {
// if (SystemInfo.isWindows) {
// File file = NodeFinder.resolvePath(projectRoot, NodeFinder.NODE_MODULES, ".bin", exe);
// if (file.exists()) {
// return file;
// }
// } else {
// File file = NodeFinder.resolvePath(projectRoot, NodeFinder.NODE_MODULES, "grunt-packages", "bin", "packages.js");
// if (file.exists()) {
// return file;
// }
// }
// return null;
// }
/**
* search for projectRoot/node_modules/.bin/exe
* @param projectRoot node modules root
* @param exe exe to find
* @return file
*/
public static File findExeInProjectBin(File projectRoot, String exe) {
File file = NodeFinder.resolvePath(projectRoot, NodeFinder.NODE_MODULES, ".bin", exe);
if (file.exists()) {
return file;
}
return null;
}
}