Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 68c602a

Browse files
Fetch versions directly from npm registry url (#57)
* Fetch versions directly from npm registry url * Support alternative npm urls + error handling * Add interface file * Improve error handling some more
1 parent ec46775 commit 68c602a

File tree

5 files changed

+101
-26
lines changed

5 files changed

+101
-26
lines changed

‎src/ErrorUtils.res

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
let getErrorMessage = exn =>
2+
switch exn->Exn.message {
3+
| Some(message) => message
4+
| None => exn->String.make
5+
}

‎src/NpmRegistry.res

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
type response = {
2+
ok: bool,
3+
status: int,
4+
json: unit => promise<Js.Json.t>,
5+
}
6+
7+
@val external fetch: string => promise<response> = "fetch"
8+
9+
@scope(("process", "env"))
10+
external npm_config_registry: option<string> = "NPM_CONFIG_REGISTRY"
11+
12+
@inline
13+
let defaultRegistryUrl = "https://registry.npmjs.org"
14+
15+
let getNpmRegistry = () =>
16+
npm_config_registry
17+
->Option.flatMap(registry => registry->Node.Url.make)
18+
->Option.mapOr(defaultRegistryUrl, url => url->Node.Url.href)
19+
20+
type fetchError =
21+
| FetchError({message: string})
22+
| HttpError({status: int})
23+
| ParseError
24+
25+
let getFetchErrorMessage = fetchError => {
26+
let message = switch fetchError {
27+
| FetchError({message}) => `Fetch error. Message: ${message}`
28+
| HttpError({status}) => `Http error. Status: ${status->Int.toString}`
29+
| ParseError => "Parse error."
30+
}
31+
32+
`Fetching versions from registry failed: ${message}`
33+
}
34+
35+
let getPackageVersions = async (packageName, range) => {
36+
let registryUrl = getNpmRegistry()
37+
38+
switch await fetch(`${registryUrl}/${packageName}`) {
39+
| response if response.ok =>
40+
switch await response.json() {
41+
| Object(dict) =>
42+
switch dict->Dict.get("versions") {
43+
| Some(Object(dict)) =>
44+
let versions =
45+
dict
46+
->Dict.keysToArray
47+
->Array.filterMap(version =>
48+
version->CompareVersions.satisfies(range) ? Some(version) : None
49+
)
50+
versions->Array.reverse
51+
versions->Ok
52+
53+
| _ => Error(ParseError)
54+
}
55+
| _ => Error(ParseError)
56+
}
57+
58+
| responseNotOk => Error(HttpError({status: responseNotOk.status}))
59+
| exception Exn.Error(exn) => Error(FetchError({message: exn->ErrorUtils.getErrorMessage}))
60+
}
61+
}

‎src/NpmRegistry.resi

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
type fetchError =
2+
| FetchError({message: string})
3+
| HttpError({status: int})
4+
| ParseError
5+
6+
let getFetchErrorMessage: fetchError => string
7+
8+
let getPackageVersions: (string, string) => promise<result<array<string>, fetchError>>

‎src/RescriptVersions.res

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,6 @@ let rescriptCoreVersionRange = ">=1.0.0"
55

66
type versions = {rescriptVersion: string, rescriptCoreVersion: string}
77

8-
let getPackageVersions = async (packageName, range) => {
9-
let {stdout} = await Node.Promisified.ChildProcess.exec(`npm view ${packageName} versions --json`)
10-
11-
let versions = switch JSON.parseExn(stdout) {
12-
| Array(versions) =>
13-
versions->Array.filterMap(json =>
14-
switch json {
15-
| String(version) if version->CompareVersions.satisfies(range) => Some(version)
16-
| _ => None
17-
}
18-
)
19-
| _ => []
20-
}
21-
22-
versions->Array.reverse
23-
versions
24-
}
25-
268
let getCompatibleRescriptCoreVersions = (~rescriptVersion, ~rescriptCoreVersions) =>
279
if CompareVersions.compareVersions(rescriptVersion, "11.1.0")->Ordering.isLess {
2810
rescriptCoreVersions->Array.filter(coreVersion =>
@@ -32,25 +14,36 @@ let getCompatibleRescriptCoreVersions = (~rescriptVersion, ~rescriptCoreVersions
3214
rescriptCoreVersions
3315
}
3416

17+
let spinnerMessage = "Loading available versions..."
18+
3519
let promptVersions = async () => {
3620
let s = P.spinner()
3721

38-
s->P.Spinner.start("Loading available versions...")
22+
s->P.Spinner.start(spinnerMessage)
3923

40-
let (rescriptVersions, rescriptCoreVersions) = await Promise.all2((
41-
getPackageVersions("rescript", rescriptVersionRange),
42-
getPackageVersions("@rescript/core", rescriptCoreVersionRange),
24+
let (rescriptVersionsResult, rescriptCoreVersionsResult) = await Promise.all2((
25+
NpmRegistry.getPackageVersions("rescript", rescriptVersionRange),
26+
NpmRegistry.getPackageVersions("@rescript/core", rescriptCoreVersionRange),
4327
))
4428

45-
s->P.Spinner.stop("Versions loaded.")
29+
switch (rescriptVersionsResult, rescriptCoreVersionsResult) {
30+
| (Ok(_), Ok(_)) => s->P.Spinner.stop("Versions loaded.")
31+
| _ => s->P.Spinner.stop(spinnerMessage)
32+
}
4633

47-
let rescriptVersion = switch rescriptVersions {
48-
| [version] => version
49-
| _ =>
34+
let rescriptVersion = switch rescriptVersionsResult {
35+
| Ok([version]) => version
36+
| Ok(rescriptVersions) =>
5037
await P.select({
5138
message: "ReScript version?",
5239
options: rescriptVersions->Array.map(v => {P.value: v}),
5340
})->P.resultOrRaise
41+
| Error(error) => error->NpmRegistry.getFetchErrorMessage->Error.make->Error.raise
42+
}
43+
44+
let rescriptCoreVersions = switch rescriptCoreVersionsResult {
45+
| Ok(versions) => versions
46+
| Error(error) => error->NpmRegistry.getFetchErrorMessage->Error.make->Error.raise
5447
}
5548

5649
let rescriptCoreVersions = getCompatibleRescriptCoreVersions(

‎src/bindings/Node.res

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@ module Url = {
6464
type t
6565

6666
@module("node:url") external fileURLToPath: t => string = "fileURLToPath"
67+
68+
@new external makeUnsafe: string => t = "URL"
69+
@get external href: t => string = "href"
70+
71+
let make = string =>
72+
try Some(makeUnsafe(string)) catch {
73+
| Exn.Error(_exn) => None
74+
}
6775
}
6876

6977
module Os = {

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /