You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
147 lines
3.7 KiB
147 lines
3.7 KiB
module VersionSet = Set.Make(String); |
|
|
|
module Local = { |
|
type t = { |
|
name: string, |
|
fullPath: string, |
|
}; |
|
}; |
|
|
|
exception Version_not_found(string); |
|
|
|
module Remote = { |
|
type t = { |
|
name: string, |
|
baseURL: string, |
|
installed: bool, |
|
}; |
|
|
|
let skip = (~amount, str) => |
|
Str.last_chars(str, String.length(str) - amount); |
|
|
|
let parseSemver = version => version |> skip(~amount=1) |> Semver.of_string; |
|
|
|
let compare = (v1, v2) => |
|
switch (parseSemver(v1), parseSemver(v2)) { |
|
| (Some(v1), Some(v2)) => Semver.compare(v1, v2) |
|
| (None, _) |
|
| (_, None) => - Core.String.compare(v1, v2) |
|
}; |
|
|
|
let getInstalledVersionSet = () => |
|
Fs.readdir(Directories.nodeVersions) |
|
|> Result.fold(_ => [||], x => x) |
|
|> Array.fold_left( |
|
(acc, curr) => VersionSet.add(curr, acc), |
|
VersionSet.empty, |
|
); |
|
|
|
let getRelativeLinksFromHTML = html => |
|
Soup.parse(html) |
|
|> Soup.select("pre a") |
|
|> Soup.to_list |
|
|> List.map(Soup.attribute("href")) |
|
|> Core.List.filter_map(~f=x => x); |
|
|
|
let downloadFileSuffix = ".tar.gz"; |
|
|
|
let getVersionFromFilename = filename => { |
|
let strings = filename |> String.split_on_char('-'); |
|
List.nth(strings, 1); |
|
}; |
|
}; |
|
|
|
let format = version => { |
|
let version = |
|
switch (Str.first_chars(version, 1) |> Int32.of_string) { |
|
| _ => "v" ++ version |
|
| exception _ => version |
|
}; |
|
|
|
version; |
|
}; |
|
|
|
let endsWith = (~suffix, str) => { |
|
let suffixLength = String.length(suffix); |
|
|
|
String.length(str) > suffixLength |
|
&& Str.last_chars(str, suffixLength) == suffix; |
|
}; |
|
|
|
exception No_Download_For_System(System.NodeOS.t, System.NodeArch.t); |
|
|
|
let getFileToDownload = (~version as versionName, ~os, ~arch) => { |
|
let versionName = |
|
switch (Str.first_chars(versionName, 1) |> Int32.of_string) { |
|
| _ => "v" ++ versionName |
|
| exception _ => versionName |
|
}; |
|
let url = "https://nodejs.org/dist/" ++ versionName ++ "/"; |
|
let%lwt html = |
|
try%lwt (Http.makeRequest(url) |> Lwt.map(Http.body)) { |
|
| Http.Not_found(_) => Lwt.fail(Version_not_found(versionName)) |
|
}; |
|
let filenames = |
|
html |
|
|> Remote.getRelativeLinksFromHTML |
|
|> List.filter( |
|
endsWith( |
|
~suffix= |
|
System.NodeOS.toString(os) |
|
++ "-" |
|
++ System.NodeArch.toString(arch) |
|
++ Remote.downloadFileSuffix, |
|
), |
|
); |
|
|
|
switch (filenames |> List.hd) { |
|
| x => Lwt.return(url ++ x) |
|
| exception _ => Lwt.fail(No_Download_For_System(os, arch)) |
|
}; |
|
}; |
|
|
|
let getCurrentVersion = () => |
|
switch (Fs.realpath(Directories.currentVersion)) { |
|
| installationPath => |
|
let fullPath = Filename.dirname(installationPath); |
|
Some(Local.{fullPath, name: Core.Filename.basename(fullPath)}); |
|
| exception (Unix.Unix_error(_, _, _)) => None |
|
}; |
|
|
|
let getInstalledVersions = () => |
|
Fs.readdir(Directories.nodeVersions) |
|
|> Result.map(x => { |
|
Array.sort(Remote.compare, x); |
|
x; |
|
}) |
|
|> Result.map( |
|
Array.map(name => |
|
Local.{ |
|
name, |
|
fullPath: Filename.concat(Directories.nodeVersions, name), |
|
} |
|
), |
|
); |
|
|
|
let getRemoteVersions = () => { |
|
let%lwt bodyString = |
|
Http.makeRequest("https://nodejs.org/dist/") |> Lwt.map(Http.body); |
|
|
|
let versions = bodyString |> Remote.getRelativeLinksFromHTML; |
|
let installedVersions = Remote.getInstalledVersionSet(); |
|
|
|
versions |
|
|> Core.List.filter(~f=x => |
|
Str.last_chars(x, 1) == "/" && Str.first_chars(x, 1) != "." |
|
) |
|
|> Core.List.map(~f=x => Str.first_chars(x, String.length(x) - 1)) |
|
|> List.sort(Remote.compare) |
|
|> List.map(name => |
|
Remote.{ |
|
name, |
|
installed: VersionSet.find_opt(name, installedVersions) != None, |
|
baseURL: "https://nodejs.org/dist/" ++ name ++ "/", |
|
} |
|
) |
|
|> Lwt.return; |
|
}; |