package requirejs;
import com.intellij.javascript.nodejs.library.NodeJsCoreModulesCatalog;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
public class Path {
protected PsiElement element;
protected RequirejsProjectComponent component;
protected String originValue;
protected String path;
protected String module = null;
public static final List<String> MODULES_SKIPPED_RESOLVING = Arrays.asList(
"goog",
"font"
);
public Path(PsiElement element, RequirejsProjectComponent component) {
this.element = element;
this.component = component;
parse(element.getText());
}
public void parse(String rawValue) {
originValue = rawValue.replace("\"", "").replace("'", "");
if (originValue.contains("!")) {
String[] exclamationMarkSplit = originValue.split("!");
if (exclamationMarkSplit.length == 2) {
module = exclamationMarkSplit[0];
path = exclamationMarkSplit[1];
} else {
module = exclamationMarkSplit[0];
path = "";
}
} else {
path = originValue;
}
}
public String getOriginValue() {
return originValue;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
@Nullable
public String getModule() {
return module;
}
public boolean isAbsolutePath() {
return path.startsWith("/") && !path.startsWith("//");
}
public boolean isRelativePath() {
return path.startsWith(".");
}
public boolean isBuildInVariable() {
return originValue.equals("exports") || originValue.equals("module") || originValue.equals("require");
}
@Nullable
public PsiElement resolve() {
PsiElement result;
if (this.isBuildInVariable()) {
return getContainingFile();
}
if (this.isAbsolutePath()) {
return resolveAbsolutePath();
} else if (this.isRelativePath()) {
result = probeResolveRelativePath();
if (null != result) {
return result;
}
}
result = probeResolveUrl();
if (null != result) {
return result;
}
result = probeResolveBasic();
if (null != result) {
return result;
}
result = probeResolveRequirePath();
if (null != result) {
return result;
}
result = probeResolveRequireAlias();
if (null != result) {
return result;
}
result = probeResolvePackage();
if (null != result) {
return result;
}
component.getLogger().debug("Could not resolve reference for " + this.getOriginValue());
return null;
}
@Nullable
protected PsiElement probeResolveUrl() {
if (this.getPath().startsWith("http") || this.getPath().startsWith("//")) {
return new PsiUriElement(this.element, this.getPath());
}
return null;
}
@Nullable
protected PsiElement probeResolvePackage() {
for (Package pkg : component.packageConfig.packages) {
if (this.getPath().startsWith(pkg.name)) {
VirtualFile targetFile;
String moduleFilePath;
if (this.getPath().equals(pkg.name)) {
moduleFilePath = pkg.main;
} else {
moduleFilePath = this.getPath().replace(pkg.name, "");
}
targetFile = component.getBaseUrlPath(false)
.findFileByRelativePath(pkg.location + "/" + moduleFilePath + ".js");
if (null != targetFile) {
return getPsiManager().findFile(targetFile);
}
}
}
return null;
}
@Nullable
protected PsiElement probeResolveRequireAlias() {
String requireMapModule = FileUtils.removeExt(element.getContainingFile().getVirtualFile().getPath().replace(
component.getWebDir(this.getElementFile()).getPath() + '/',
""
), ".js");
RequirePathAlias alias = component.requireMap.getAliasByModule(requireMapModule, this.getPath());
if (null != alias) {
VirtualFile targetFile = FileUtils.findFileByPath(component.getWebDir(getElementFile()), alias.path);
if (null != targetFile) {
return getPsiManager().findFile(targetFile);
}
}
return null;
}
@Nullable
protected PsiElement probeResolveRequirePath() {
return component.requirePaths.resolve(this);
}
@NotNull
protected PsiManager getPsiManager() {
return PsiManager.getInstance(element.getProject());
}
@Nullable
protected PsiElement probeResolveBasic() {
VirtualFile baseUrl = component.getBaseUrlPath(true);
if (null != baseUrl) {
VirtualFile targetFile = FileUtils.findFileByPath(baseUrl, this.getPath());
if (null != targetFile) {
return getPsiManager().findFile(targetFile);
} else if (null != this.getModule()) {
if (isSkippedModule()) {
return this.getContainingFile();
}
if (NodeJsCoreModulesCatalog.INSTANCE.isPublicCoreModule(this.getPath())) {
return this.getContainingFile();
}
String modulePath = this.getPath().concat(".").concat(this.getModule());
targetFile = FileUtils.findFileByPath(baseUrl, modulePath);
if (null != targetFile) {
return getPsiManager().findFile(targetFile);
}
}
}
return null;
}
protected boolean isSkippedModule() {
return MODULES_SKIPPED_RESOLVING.contains(this.getModule());
}
@Nullable
protected PsiElement probeResolveRelativePath() {
VirtualFile targetFile;
PsiDirectory fileDirectory = element.getContainingFile().getContainingDirectory();
if (null != fileDirectory) {
targetFile = FileUtils.findFileByPath(fileDirectory.getVirtualFile(), this.getPath());
if (null != targetFile) {
return getPsiManager().findFile(targetFile);
}
}
return null;
}
@Nullable
protected PsiElement resolveAbsolutePath() {
VirtualFile targetFile = FileUtils.findFileByPath(component.getWebDir(getElementFile()), this.getPath());
if (null != targetFile) {
return getPsiManager().findFile(targetFile);
} else {
return null;
}
}
protected VirtualFile getElementFile() {
return element
.getContainingFile()
.getOriginalFile()
.getVirtualFile();
}
protected PsiElement getContainingFile() {
return element.getContainingFile();
}
}